2014 csu校赛 I 题,比赛的时候拿着他看了几个小时愣是没弄出好的方法,我们也想过统计出每个root的节点总数,然后减去离它d层的子节点的数目,即为答案。但是因为树的存储是无序的,所以每次为了找到子节点还是需要搜索,这肯定是承受不了的。
事实上,这个思路大体是对的,只是需要进行预处理,使得在找离它为d层的子节点能尽量节约时间,此外,如果找到了子节点挨个去减,也是很费时的,因为这些子节点必定处在同一层,他们的bfs序是连续的,故比较容易想到用前缀和来快速求得某一层的所有儿子的数目。
我们看这样的树
因此我们如果能把每个root的dfs时间戳的最小值和最大值给记录一下,再求出bfs序,则,对于某一层的所有节点,只要是时间戳在该root的最小值和最大值范围内,则必定是我们要求得孩子节点。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <vector> 6 #define N 100010 7 using namespace std; 8 vector <int> deep_num[N]; 9 int u[N],v[N],nt[N],ft[N],cnt,dfs_clock; 10 int last[N][2],max_deep[N],tot[N],deep[N],sum[N]; 11 void add(int a,int b){ 12 u[cnt]=a; 13 v[cnt]=b; 14 nt[cnt]=ft[a]; 15 ft[a]=cnt++; 16 } 17 void init(int num) 18 { 19 cnt=0; 20 dfs_clock=0; 21 sum[0]=0; 22 23 for (int i=0;i<=num;i++){ 24 ft[i]=-1; 25 nt[i]=0; 26 u[i]=v[i]=0; 27 deep_num[i].clear(); 28 deep_num[i].push_back(0); //先给每个层预先加一个0,方便之后处理 29 } 30 } 31 void dfs(int u,int d) 32 { 33 last[u][0]=++dfs_clock; 34 deep_num[d].push_back(u);//这里直接把节点存入相应的层,省去了BFS,直接达到了计算BFS序的作用 35 tot[u]=1; 36 deep[u]=d; 37 max_deep[u]=d; 38 for (int w=ft[u];w>=0;w=nt[w]){ 39 int x=v[w]; 40 dfs(x,d+1); 41 tot[u]+=tot[x];//计算root节点下的节点总数 42 max_deep[u]=max(max_deep[u],max_deep[x]);//记录该root最大层有多深 43 } 44 last[u][1]=dfs_clock; 45 if (deep_num[deep[u]].size()>1) 46 sum[u]=sum[deep_num[deep[u]][deep_num[deep[u]].size()-2]]+tot[u];//因为没有进行BFS,所以计算前缀和要稍微麻烦一点,根据存贮好的每一层的节点来计算 47 else 48 sum[u]=sum[deep_num[deep[u]-1][deep_num[deep[u]-1].size()-1]]+tot[u]; 49 } 50 int query(int x,int d) 51 { 52 if (max_deep[x]<=deep[x]+d-1)//如果该root没有那一层的孩子,则可以直接返回,因为肯定不需要减孩子了 53 return tot[x]; 54 int val=last[x][0]; 55 int ret=tot[x]; 56 int nd=deep[x]+d; 57 int L=0,R=deep_num[nd].size()-1,mid; 58 int a,b; 59 while (L<R)// 60 { 61 62 mid=(L+R)/2; 63 //cout<<L<<" "<<R<<" "<<mid<<endl; 64 int nx=deep_num[nd][mid]; 65 if (last[nx][0]>=val) R=mid; 66 else L=mid+1; 67 //cout<<nx<<" !!! "<<endl; 68 } 69 //cout<<"pass"<<endl; 70 a=deep_num[nd][L-1];算出左边界,因为要算前缀和,所以L-1,这样前面的push 0在这里也起到作用了 71 val=last[x][1]; 72 L=0,R=deep_num[nd].size()-1; 73 while (L<R) 74 { 75 mid=R-(R-L)/2; 76 int nx=deep_num[nd][mid]; 77 if (last[nx][1]<=val) L=mid; 78 else R=mid-1; 79 // cout<<nx<<" ~~ "<<endl; 80 } 81 b=deep_num[nd][R]; 82 //cout<<a<<" ab "<<b<<endl; 83 //cout<<sum[a]<<" sum "<<sum[b]<<endl; 84 ret-=sum[b]-sum[a]; //减去前缀和,其实还有个细节就是因为刚刚已经判断了,所以一定有孩子在这一层,所以这样减一定是合理的,不会出现没孩子也减掉的情况 85 return ret; 86 87 } 88 int main() 89 { 90 int t,n,m,x,d; 91 scanf("%d",&t); 92 while (t--){ 93 scanf("%d",&n); 94 init(n); 95 for (int i=2;i<=n;i++){ 96 int tmp; 97 scanf("%d",&tmp); 98 add(tmp,i); 99 } 100 dfs(1,1); 101 scanf("%d",&m); 102 while (m--){ 103 scanf("%d%d",&x,&d); 104 int ans=query(x,d); 105 printf("%d ",ans); 106 } 107 } 108 return 0; 109 }