【树状数组】CSU 1811 Tree Intersection (2016湖南省第十二届大学生计算机程序设计竞赛)
题目链接:
http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1811
题目大意:
一棵树,N(2<=N<=105)个节点,每个节点有一种颜色Ci(Ci<=N),问把每一条边删掉后,剩下的两个联通块中颜色的交集的大小(就是两边都含有的颜色种数)。
题目思路:
【树状数组】
我的数据结构造诣不深,这题写了暴力求每个点的颜色数T了。看了别人的题解写了超级久WA了好多才过。
首先可以知道,如果已知每个节点的子树中含有的颜色种数C和只出现在这棵子树的颜色种数D,那么这个节点和它的父亲节点中间的那条边被删去后的答案就是C-D。
所以问题变为求以每个节点为根的子树中的C和D值。然后我想到这里就不知道要怎么写了。看了大神的题解才有一点思路。(不过我感觉大神的题解好像有点问题??)
首先转化成dfs序,按照遍历这棵树的顺序求出每个节点的新的从小到大的标号q[u].b(父亲标号先前于儿子),同时记下原先对应的节点标号q[u].id
预处理出对于颜色ci,记pre[ci]为ci上次出现的颜色位置,L[ci],R[ci]为ci出现的最左最右端。然后按照新的dfs序标号从小到大做
对于节点i,将pre[ci]到i之间的所有点ans+1(因为答案是一条条树链统计的,不会计算到兄弟节点,实际影响的只有这个节点以上的父亲节点,所以答案不会出错,本质上相当于把L[ci]到R[ci]所有的点都ans+1,但是只有有这种颜色的节点到根的树链上的点会加上答案,这样其实是没错的)
如果到了颜色的最右端,把这种颜色最左端左边的答案删掉(因为一开始pre默认是0,会把1~L[ci]中的答案+1,需要扣除,也可以一开始就从L[ci]开始加)
到达叶子结点后统计这条树链的答案。以上的ans可以用树状数组统计。
1 // 2 //by coolxxx 3 //#include<bits/stdc++.h> 4 #include<iostream> 5 #include<algorithm> 6 #include<string> 7 #include<iomanip> 8 #include<map> 9 #include<stack> 10 #include<queue> 11 #include<set> 12 #include<bitset> 13 #include<memory.h> 14 #include<time.h> 15 #include<stdio.h> 16 #include<stdlib.h> 17 #include<string.h> 18 //#include<stdbool.h> 19 #include<math.h> 20 #define min(a,b) ((a)<(b)?(a):(b)) 21 #define max(a,b) ((a)>(b)?(a):(b)) 22 #define abs(a) ((a)>0?(a):(-(a))) 23 #define lowbit(a) (a&(-a)) 24 #define sqr(a) ((a)*(a)) 25 #define swap(a,b) ((a)^=(b),(b)^=(a),(a)^=(b)) 26 #define mem(a,b) memset(a,b,sizeof(a)) 27 #define eps (1e-10) 28 #define J 10000 29 #define mod 1000000007 30 #define MAX 0x7f7f7f7f 31 #define PI 3.14159265358979323 32 #pragma comment(linker,"/STACK:102400000,102400000") 33 #define N 100004 34 #define M 200004 35 using namespace std; 36 typedef long long LL; 37 int cas,cass; 38 int n,m,lll,ans; 39 double anss; 40 LL aans; 41 int last[N],fa[N],c[N],pre[N],l[N],r[N],f[N],g[N],e[N]; 42 struct edge 43 { 44 int from,next,to; 45 }a[M]; 46 struct xu 47 { 48 int b,id; 49 }q[N]; 50 void add(int x,int y) 51 { 52 a[++lll].to=y; 53 a[lll].from=x; 54 a[lll].next=last[x]; 55 last[x]=lll; 56 } 57 bool cmp(xu aa,xu bb) 58 { 59 return aa.b<bb.b; 60 } 61 void dfs(int u,int father) 62 { 63 int i,v; 64 q[u].b=++cas,q[u].id=u;//b为新的按照dfs顺序的标号,id为原先标号 65 for(i=last[u];i;i=a[i].next) 66 { 67 v=a[i].to; 68 if(v==father)continue; 69 dfs(v,u); 70 e[q[v].b]=(i+1)>>1;//把新节点标号和原先的边的标号对应起来 71 fa[q[v].b]=q[u].b;//统计新节点标号的父亲 72 } 73 } 74 inline void modify(int x,int y) 75 { 76 int i; 77 for(i=x;i<=n;i+=lowbit(i))f[i]+=y; 78 } 79 inline int getsum(int x) 80 { 81 int i,sum=0; 82 for(i=x;i;i-=lowbit(i))sum+=f[i]; 83 return sum; 84 } 85 int main() 86 { 87 #ifndef ONLINE_JUDGE 88 freopen("1.txt","r",stdin); 89 // freopen("2.txt","w",stdout); 90 #endif 91 int i,j,k; 92 int x,y,z; 93 // init(); 94 // for(scanf("%d",&cass);cass;cass--) 95 // for(scanf("%d",&cas),cass=1;cass<=cas;cass++) 96 // while(~scanf("%s",s)) 97 while(~scanf("%d",&n)) 98 { 99 lll=cas=0;mem(last,0);mem(l,0);mem(r,0);mem(pre,0);mem(f,0); 100 for(i=1;i<=n;i++)scanf("%d",c+i); 101 for(i=1;i<n;i++) 102 { 103 scanf("%d%d",&x,&y); 104 add(x,y),add(y,x); 105 } 106 dfs(1,0); 107 sort(q+1,q+1+n,cmp); 108 for(i=1,j=q[i].id;i<=n;i++,j=q[i].id) 109 if(!l[c[j]])l[c[j]]=i;//统计每种颜色出现的最左端 110 for(i=n,j=q[i].id;i;i--,j=q[i].id) 111 if(!r[c[j]])r[c[j]]=i;//统计每种颜色出现的最右端 112 for(i=1;i<=n;i++) 113 { 114 j=q[i].id; 115 modify(pre[c[j]]+1,1),modify(i+1,-1);//把上次这个颜色出现的位置到i之间的节点答案+1 116 pre[c[j]]=i;//pre记录的是这个颜色上一次出现的位置 117 if(i==r[c[j]])modify(1,-1),modify(l[c[j]]+1,1);//如果这种颜色出现最右端在这个点,那么把最左端左边的所有节点答案-1,因为颜色最左端的答案不能统计在内 118 if(a[last[j]].to==q[fa[i]].id)//如果当前结点是叶子结点 119 for(k=i;k!=1;k=fa[k])g[e[k]]=getsum(k);//统计从根到这个叶子结点的树链的答案 120 } 121 for(i=1;i<n;i++) 122 printf("%d\n",g[i]); 123 } 124 return 0; 125 } 126 /* 127 // 128 129 // 130 */