hdu 6035 Colorful Tree(树形dp+技巧)

题目链接:hdu 6035 Colorful Tree

题意:

给你一棵树,每个节点有一种颜色,现在让你求所有点对的路径上不同的颜色数量的总和。

题解:

下面是官方题解:

单独考虑每一种颜色,答案就是对于每种颜色至少经过一次这种的路径条数之和。反过来思考只需要求有多少条路径没有经过这种颜色即可。直接做可以采用虚树的思想(不用真正建出来),对每种颜色的点按照 dfs 序列排个序,就能求出这些点把原来的树划分成的块的大小。这个过程实际上可以直接一次 dfs 求出。

这里的所说的单独考虑每种颜色,指的是对这个树开一个滤镜,比如对于这棵树,我只看颜色1,不是颜色1的点全部看为无色。

如果这棵树上的点全是颜色1,那么颜色1对答案的贡献就是这棵树的点对数n*(n-1)/2(因为每个点对的路径都有颜色1,就对的答案贡献了1)。但是并不是任意点对上的路径都有颜色1,所以对于不经过颜色1的联通块,我们要减去这个多算的联通块。

然后再推广到有n种颜色,每个颜色都要这样处理一下,这里可以用一个dfs直接一起处理全部颜色的情况,所以就能O(n)搞了。

 1 #include<bits/stdc++.h>
 2 #define F(i,a,b) for(int i=(a);i<=(b);++i)
 3 using namespace std;
 4 typedef long long ll;
 5 
 6 const int N=2e5+7;
 7 int n,c[N],x,y,cas,pos[N],cur[N];
 8 vector<int>g[N];
 9 ll ans,rem[N];
10 
11 inline ll sum(ll x){return x*(x-1)>>1;}
12 
13 int dfs(int x=1,int fa=0)
14 {
15     int sz=1,szv,pre=pos[c[x]];
16     pos[c[x]]=x;
17     for(int &it:g[x])if(it!=fa)
18     {
19         cur[x]=0,szv=dfs(it,x);
20         sz+=szv,ans-=sum(szv-cur[x]);
21     }
22     pre?cur[pre]+=sz:rem[c[x]]+=sz;
23     pos[c[x]]=pre;
24     return sz;
25 }
26 
27 int main(){
28     while(~scanf("%d",&n))
29     {
30         F(i,1,n)scanf("%d",c+i),g[i].clear(),rem[i]=0;
31         F(i,2,n)
32         {
33             scanf("%d%d",&x,&y);
34             g[x].push_back(y);
35             g[y].push_back(x);
36         }
37         ans=sum(n)*n,dfs();
38         F(i,1,n)ans-=sum(n-rem[i]);
39         printf("Case #%d: %lld\n",++cas,ans);
40     }
41     return 0;
42 }
View Code

 

posted @ 2017-07-26 13:33  bin_gege  阅读(188)  评论(0编辑  收藏  举报