题目链接:http://codeforces.com/contest/351/problem/D
题目大意:n个数,col[i]对应第i个数的颜色,并给你他们之间的树形关系(以1为根),有m次询问,每次给出vi,ki,要求找出以点vi为根的子树上出现超过ki次的颜色数。
解题思路:这题显然是可以用莫队写的,只要在开一个数组cnk[i]记录出现次数超过i次的颜色数即可,但是要先进行“将树化为线段的操作”,之前写的一道线段树也用了dfs序的方法使得多叉树化为线段:链接,这里就不多说了。要注意的是使用dfs划线的过程中记得要把对应序号的颜色也映射到线段上。
1 #include<iostream> 2 #include<algorithm> 3 #include<vector> 4 #include<cstdio> 5 #include<cmath> 6 using namespace std; 7 const int N=1e5+5; 8 int num; 9 int unit; 10 int arr[N],col[N],cnt[N],cnk[N],res[N],Start[N],End[N],vis[N];//cnk[i]表示至少出现i次的颜色数 11 vector<int>v[N]; 12 13 struct node{ 14 int l,r,kj; 15 int id; 16 }q[N]; 17 18 //将树化为区间 19 void dfs(int rt){ 20 Start[rt]=++num; 21 //**将对应序号的颜色映射到新的线段上 22 arr[num]=col[rt]; 23 vis[rt]=1; 24 for(int i=0;i<v[rt].size();i++){ 25 if(!vis[v[rt][i]]) 26 dfs(v[rt][i]); 27 } 28 End[rt]=num; 29 } 30 31 bool cmp(node a,node b){ 32 return a.l/unit==b.l/unit?a.r<b.r:a.l/unit<b.l/unit; 33 } 34 35 void add(int pos){ 36 cnt[arr[pos]]++; 37 cnk[cnt[arr[pos]]]++; 38 } 39 40 void remove(int pos){ 41 cnk[cnt[arr[pos]]]--; 42 cnt[arr[pos]]--; 43 } 44 45 int main(){ 46 int n,m; 47 scanf("%d%d",&n,&m); 48 unit=sqrt(n); 49 for(int i=1;i<=n;i++){ 50 scanf("%d",&col[i]); 51 } 52 for(int i=1;i<=n-1;i++){ 53 int a,b; 54 scanf("%d%d",&a,&b); 55 //**题目没有直接说明a,b从属关系所以建立双向邻接表 56 v[a].push_back(b); 57 v[b].push_back(a); 58 } 59 //以1为根 60 dfs(1); 61 62 for(int i=1;i<=m;i++){ 63 int vj,kj; 64 scanf("%d%d",&vj,&kj); 65 q[i].kj=kj; 66 q[i].id=i; 67 q[i].l=Start[vj]; 68 q[i].r=End[vj]; 69 } 70 sort(q+1,q+1+m,cmp); 71 int L=q[1].l,R=L-1; 72 for(int i=1;i<=m;i++){ 73 while(L>q[i].l) 74 add(--L); 75 while(L<q[i].l) 76 remove(L++); 77 while(R<q[i].r) 78 add(++R); 79 while(R>q[i].r) 80 remove(R--); 81 res[q[i].id]=cnk[q[i].kj]; 82 } 83 for(int i=1;i<=m;i++){ 84 printf("%d ",res[i]); 85 } 86 } 87