Blood Cousins Return
阿新 • • 發佈:2021-07-15
[https://codeforces.com/problemset/problem/246/E] (點此看題)
簡要題面:
一棵樹上有n個節點,每個節點有對應的名字(名字可重複)。
每次詢問,求深度比$vi$多$ki$的$vi$的兒子中,有多少種名字
分析:
Step1:
我們可以懂$DFS$輕鬆找到每個節點的深度dep[x], 同時用$DFS$序列得知每個節點間的關係(也就是說,可以用in[x]與ou[x],來知道另一個節點$v$是不是$x$的兒子)。
做完以上工作,本題所求的結果即是 已知深度dep,求in[x]在in[father]~ou[father]中的$x$,他們名字共有多少種
Stepp:
要求一段區間中不同的種類,我們可以聯想到**HH的項鍊**[https://www.luogu.com.cn/problem/P1972] (點此進入)
我們可以用很多方法來完成這道題,像**莫隊、主席樹、樹狀陣列等** 這道題我用的是樹狀陣列 先貼一個HH的項鍊的code吧
#include<bits/stdc++.h> using namespace std; #define re register int const int N=1e6+5; int n, m, a[N], c[N]; inline int lowbit(const int x) {return x&-x;} inlineView Codeint getsum(const int x) { int ret=0; for(re i=x;i > 0;i-=lowbit(i)) ret += c[i]; return ret; } inline void modi(const int x,const int d) { for(re i=x;i<=1000000;i+=lowbit(i)) c[i] += d; } struct node{ int a, b, id; bool operator<(const node&x)const{ return x.b > b; //右端點從小到大排序 } }ask[N]; int lst[N], ans[N]; // lst[i]表示 顏色i最近一次出現的位置 signed main() { scanf("%d",&n); for(re i=1;i<=n;++i) scanf("%d",&a[i]); scanf("%d",&m); for(re i=1;i<=m;++i) { scanf("%d%d",&ask[i].a,&ask[i].b); ask[i].id = i; } sort(ask+1, ask+1+m); // 將詢問區間 由右端點從小到大 排序 for(re i=1, k=1;i<=n && k<=m;++i) { modi(i, 1); // 每次到一個新的點,將區間[i,n] +1 if(lst[a[i]]) modi(lst[a[i]], -1); // 如果之前已經有過這個顏色 ,將曾經的顏色位置 -1 // 正確性:我們已經將詢問排了序,所以以後的詢問只需要離右端點近的顏色 lst[a[i]] = i; while(k <= m && ask[k].b == i) { ans[ask[k].id] = getsum(i)- getsum(ask[k].a-1); // 注意減一 k++; } } for(re i=1;i<=m;++i) printf("%d\n", ans[i]); }
該題程式碼
#include<bits/stdc++.h> using namespace std; #define re register int const int N=1e6+5, M=4e6; map<string, int>mp; vector<int>V[N]; int n, total, in[N], ou[N], dep[N]; int tt, las[N], nt[M], ed[M], bel[N], maxdep; inline void add(const int x, const int y) { ed[++tt]=y; nt[tt]=las[x]; las[x]=tt; } void DFS(const int x,const int FA) { in[x] = ++total; bel[total] = x; dep[x] = dep[FA] + 1; maxdep = max(maxdep,dep[x]); if(V[dep[x]].size() == 0) V[dep[x]].push_back(-1); V[dep[x]].push_back(in[x]); for(re i=las[x];i;i=nt[i]) DFS(ed[i], x); ou[x] = total; } struct node{int a, b, id;}ask[N]; vector<int>TO[N]; bool cmp(const int &x, const int &y) { return ask[x].b < ask[y].b; } int a[N], c[N], lst[N], ans[N]; inline int lowbit(const int x) {return x&-x;} inline int getsum(const int x) { int ret=0; for(re i=x;i > 0;i-=lowbit(i)) ret += c[i]; return ret; } inline void modi(const int x,const int d) { for(re i=x;i<=n;i+=lowbit(i)) c[i] += d; } inline void solve(const int de) // 單獨處理每一深度de { memset(lst, 0, sizeof(lst)); memset(c, 0 ,sizeof(c)); sort(TO[de].begin()+1, TO[de].end(), cmp); int nn = V[de].size()-1, mm=TO[de].size()-1; for(re i=1,k=1; i<=nn && k<=mm; ++i) { modi(i, 1); // 樹狀陣列 int rea = bel[V[de][i]]; int color = a[rea]; if(lst[color] != 0) modi(lst[color], -1); lst[color] = i; while(k<=mm && ask[TO[de][k]].b == i) { int tp = TO[de][k]; ans[ask[tp].id] = getsum(i) - getsum(ask[tp].a-1); k++; } } } signed main() { ios::sync_with_stdio(false); cin>>n; int gf=2e5, m; for(re i=1, getname=0;i<=n;++i) { string name; int father; cin>>name>>father; if(father != 0) add(father, i); else add(gf, i); if(!mp.count(name)) mp[name] = ++getname; // 記錄每種名字所代表的數字 a[i] = mp[name]; } dep[0]=-1; DFS(gf, 0); // 一個虛擬的根節點,使森林變成一棵樹 cin>>m; for(re i=1;i<=m;++i) { int x, y; cin>>x>>y; int dd = dep[x] + y; if(dd <= maxdep && V[dd].size()) { int l = lower_bound(V[dd].begin()+1, V[dd].end(), in[x])-V[dd].begin(); int r = upper_bound(V[dd].begin()+1, V[dd].end(), ou[x])-V[dd].begin()-1; // 記錄在dd深度中,從l到r位 if(l <= r && 1 <= l && r < V[dd].size()) { ask[i]=(node){l, r, i}; if(TO[dd].size() == 0) TO[dd].push_back(-1); TO[dd].push_back(i); } } } for(re i=1;i<=n;++i) if(TO[i].size()) solve(i); for(re i=1;i<=m;++i) cout<<ans[i]<<endl; }
made by kzsm