解题报告:HDU_6035 Colorful Tree 树上计数

来源:互联网 发布:替代windows cmd 编辑:程序博客网 时间:2024/05/17 08:16

题目链接


题意:

给定一棵树,树上每个结点都有一种颜色,询问路上任意两点之间的路径上包含的不同颜色数目和。


思路:
正着求不好求,先认为所有的路径上包含所有出现过的颜色,那么只需要减去每个颜色没有出现过的路径之和。


官方题解:

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


代码:

#include<bits/stdc++.h>const int N = 2e5+10;using namespace std;int n;vector<int>G[N];int col[N],num[N],son[N];bool used[N];long long ans ;void dfs(int x,int fa = 0){   son[x] = 1;   int& c = col[x];   int last = num[c] , all = 0;   for(int i=0;i<G[x].size();i++){      int j = G[x][i];      if(j!=fa){         dfs(j,x);         son[x] += son[j];         all += num[c] - last;         int cnt = son[j] - num[c] + last;         last = num[c];         ans -= 1LL * cnt * (cnt-1) / 2;      }   }num[c] += son[x] - all ;}int main(){   int cas = 0;   while(scanf("%d",&n)==1){      ans = 0;      memset(used,0,sizeof(used));      for(int i=1;i<=n;i++){         G[i].clear();         scanf("%d",&col[i]);         num[col[i]] = 0;         used[col[i]] = 1;      }for(int i=1,s,e;i<n;i++){         scanf("%d%d",&s,&e);         G[s].emplace_back(e);         G[e].emplace_back(s);      }dfs(1);int cnt = 1;      for(int i=1;i<=n;i++)if(used[i]&&i!=col[1]){         cnt ++;         int tmp = n - num[i];         ans -= 1LL * tmp * (tmp-1) / 2;      }ans += 1LL * n * (n-1) / 2 * cnt ;      printf("Case #%d: %I64d\n",++cas,ans);   }return 0;}