http://codeforces.com/problemset/problem/241/F
题意: 有一片森林,点权,每次循环某个点的第 k k k层儿子中,点权的种数。
解析:
森林:让原坐标加一,让 1 1 1成为虚点就是一棵树了。
我按照层序遍历序号,将树变为数组,显然某个点的第 k k k层儿子在数组中连续。怎么找这个所在的区间呢?我们知道这个点的第 k k k层儿子所在层 d d d, d d d层中的最小值 L L L和最大值 R R R,在其中进行两次二分即可。
然后相当于求区间不同值的个数。这个用主席树做,以原下标作为下标。
插入第 p p p棵树首先位置 p p p加 1 1 1,如果前面出现与 p p p值相同的点,让那个点减 1 1 1。查询时,查第 r r r棵树的 [ l , r ] [l,r] [l,r]区间和即可。
补充:
二分去得到这个区间的复杂度为 O ( l o g 2 n ) O(log^2n) O(log2n),后来想到一种一个 l o g log log的做法。我们存下某个点的第一个儿子和最后一个儿子,那么就可以用倍增法得到第 k k k层儿子中的最小的和最大的了。
代码:
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
#define debug(x) cerr<<#x<<":"<<x<<endl
const int maxn=1e5+9;
#define rep_e(i,p,u) for(int i=head[p],u=to[i];i;i=nex[i],u=to[i])
int head[maxn],to[maxn<<1],nex[maxn<<1],now;
void add(int a,int b){
nex[++now]=head[a];head[a]=now;to[now]=b;
}
/*_________________________________________________________edge*/
unordered_map<string,int>M;
string x;
int n;
int val[maxn];
int dep[maxn];
int fa[maxn][20];
int rt;
int w[maxn],id[maxn],idd[maxn];
int l[maxn],r[maxn];
void bfs(){
queue<int>Q;
Q.push(rt);
int ct=0;
while(!Q.empty()){
int p=Q.front();Q.pop();
w[++ct]=val[p];
id[p]=ct;
idd[ct]=p;
rep_e(i,p,u)Q.push(u);
}
}
int root[maxn],ls[maxn*40],rs[maxn*40],siz[maxn*40];
int CT;
void insert(int last,int now,int l,int r,int pos,int val){
siz[now]=siz[last]+val;
ls[now]=ls[last];
rs[now]=rs[last];
if(l==r)return;
int mid=(l+r>>1);
if(pos<=mid)insert(ls[last],ls[now]=++CT,l,mid,pos,val);
else insert(rs[last],rs[now]=++CT,mid+1,r,pos,val);
}
int query(int last,int now,int l,int r,int L,int R){
if(l>=L&&r<=R){
return siz[now]-siz[last];
}
int mid=l+r>>1;
int res=0;
if(L<=mid)res+=query(ls[last],ls[now],l,mid,L,R);
if(R>mid)res+=query(rs[last],rs[now],mid+1,r,L,R);
return res;
}
void dfs(int p,int d){
dep[p]=d;
l[d]=min(l[d],id[p]);
r[d]=max(r[d],id[p]);
rep_e(i,p,u){
fa[u][0]=p;
dfs(u,d+1);
}
}
int vis[maxn];
void init(){
for(int i=1;(1<<i)<=n;i++){
rep(j,1,n){
fa[j][i]=fa[fa[j][i-1]][i-1];
}
}
rep(i,1,n){
insert(root[i-1],root[i]=++CT,1,n,i,1);
if(vis[w[i]]){
int tmp=root[i];
insert(tmp,root[i]=++CT,1,n,vis[w[i]],-1);
}
vis[w[i]]=i;
}
}
int getFa(int p,int k){ // id[getFa] compare id[x]
per(i,20,0){
if((1<<i)<=k){
p=fa[p][i];
k-=(1<<i);
}
}
return p;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n;
rt=1;
int ct=0;
l[1]=1e9;
rep(i,2,n+1){
l[i]=1e9;
cin>>x;
if(M.count(x))val[i]=M[x];
else val[i]=M[x]=++ct;
int fa;cin>>fa;
fa++;
add(fa,i);
}
n++;
bfs();
dfs(rt,1);
init();
int q;cin>>q;
while(q--){
int p,k;cin>>p>>k;p++;
int d=dep[p]+k;
int L=l[d],R=r[d],ansL=1,ansR=1;
while(L<=R){
int mid=L+R>>1;
if(id[getFa(idd[mid],k)]>=id[p])ansL=mid,R=mid-1;
else L=mid+1;
}
L=l[d],R=r[d];
while(L<=R){
int mid=L+R>>1;
if(id[getFa(idd[mid],k)]<=id[p])ansR=mid,L=mid+1;
else R=mid-1;
}
if(ansL<l[d]||ansL>r[d]||ansR<l[d]||ansR>r[d]||getFa(idd[ansL],k)!=p){
cout<<"0\n";
continue;
}
cout<<query(0,root[ansR],1,n,ansL,ansR)<<'\n';
}
}