题目传送门:http://acm.hdu.edu.cn/showproblem.php?pid=6035
题目大意:给你一棵树,树上每个节点都有一个颜色。 现在定义两点间的距离为两点最短路径上颜色集合大小,求该树上所有点对的距离之和。其中树上的节点个数$≤2*10^5$
如果直接处理每一条路径上颜色集合大小,显然比较麻烦。我们不妨换一种思路。
我们用S_i表示经过颜色i的路径的数量,显然答案$=sum S_i$。
考虑如何求S_i。我们先将所有颜色为i的节点全部找出来,按照dfs序排序。
显然,若树上所有的路径均经过该颜色的节点,则$S_i=frac{n*(n-1)}{2}$。
对于该点集中的节点x的每一棵子树,不妨设当前子树的根节点为v,找出所有点集中满足$dfn[v]<dfn[u]≤low[v]$且不存在$y$,使得$dfn[v]<dfn[y]<dfn[u]≤low[v]$的所有的$u$,则显然有$frac{(siz[v]-sum siz[u]) imes (siz[v]-sum siz[u]-1)}{2}$个点对不会对答案产生贡献。其中$siz[x]$表示以x为根的子树的节点个数。
该统计方法的时间复杂度为$O(n log n)$
1 #include<bits/stdc++.h> 2 #define L long long 3 #define M 200005 4 using namespace std; 5 struct edge{int u,next;}e[M*2]={0}; int head[M]={0},use=0; 6 void add(int x,int y){use++;e[use].u=y;e[use].next=head[x];head[x]=use;} 7 8 int dfn[M]={0},low[M]={0},t=0; 9 int siz[M]={0},col[M]={0}; 10 11 void dfss(int x,int fa){ 12 dfn[x]=++t; siz[x]=1; 13 for(int i=head[x];i;i=e[i].next) if(e[i].u!=fa){ 14 dfss(e[i].u,x); 15 siz[x]+=siz[e[i].u]; 16 } 17 low[x]=t; 18 } 19 bool cmp(int x,int y){ 20 if(col[x]==col[y]) return dfn[x]<dfn[y]; 21 return col[x]<col[y]; 22 } 23 int p[M]={0}; 24 L ans=0,sum=0,n; 25 26 void dfs(int &x,int y){ 27 int xx=p[x]; x++; 28 for(int i=head[xx];i;i=e[i].next) 29 if(dfn[xx]<dfn[e[i].u]){ 30 int v=e[i].u; 31 L cnt=siz[v]; 32 while(x<=y&&dfn[p[x]]<=low[v]){ 33 cnt-=siz[p[x]]; 34 dfs(x,y); 35 } 36 sum-=cnt*(cnt-1); 37 } 38 } 39 int hh=0; 40 int Main(){ 41 hh++; 42 ans=0; sum=0; t=0; 43 memset(head,0,sizeof(head)); use=0; 44 for(int i=1;i<=n;i++) scanf("%d",col+i); 45 for(int i=1;i<n;i++){ 46 int x,y; scanf("%d%d",&x,&y); 47 add(x,y); add(y,x); 48 } 49 dfss(1,0); add(0,1); 50 for(int i=1;i<=n;i++) p[i]=i; 51 sort(p+1,p+n+1,cmp); 52 for(int i=1,j;i<=n;i=j+1){ 53 for(j=i;col[p[i]]==col[p[j]];j++); j--; 54 sum=n*(n-1); 55 p[--i]=0; 56 dfs(i,j); 57 ans+=sum; 58 } 59 printf("Case #%d: %lld ",hh,ans/2); 60 //cout<<ans/2<<endl; 61 } 62 63 int main(){ 64 freopen("in.txt","r",stdin); 65 while(cin>>n) Main(); 66 }