本文共 3144 字,大约阅读时间需要 10 分钟。
31 2 11 22 361 2 1 3 2 11 21 32 42 53 6
Case #1: 6Case #2: 29
题目要求每条路径经过的不同颜色数之和,可以转换成每种颜色对答案的贡献 “有多少条路径经过一个或多个颜色x的点”可以转换成“有多少条路径不经过颜色x” 即:经过颜色x的路径数量=n(n-1)/2-不经过颜色x的路径数 怎么求不经过颜色x的路径数呢? 我们可以想象把颜色为x的点选出来,这些点会把树分成若干个子树,这些子树内的路径都是不会经过颜色为x的点的 这样我们通过dfs遍历到顶点u,颜色为x,在它的子树内所有以颜色x为根的子树都要舍去 这一做法很简单:维护一个sum[i]表示以颜色i为根的子树总大小,每次遍历u的子节点v时,sum[col[u]]就会增加, 其增加量tmp就是子树v中以颜色col[u]为根的子树大小,用sz[v]-tmp就是子树v中颜色部位col[u]的点
#includeusing namespace std;typedef long long LL;const int MX = 2e5 + 5;const LL mod = 1e9 + 7;struct Edge { int v, nxt;} E[MX * 2];int head[MX], rear;void add(int u, int v) { E[rear].v = v; E[rear].nxt = head[u]; head[u] = rear++;}int col[MX];int sz[MX];int vis[MX];LL sum[MX];LL ans;void init(int n) { for (int i = 1; i <= n; i++) head[i] = -1, vis[i] = sum[i] = 0; rear = 0;}void dfs(int u, int fa) { sz[u] = 1; LL pre = sum[col[u]], c = 0, szv; for (int i = head[u]; ~i; i = E[i].nxt) { int v = E[i].v; if (v == fa) continue; dfs(v, u); sz[u] += sz[v]; LL tmp = sum[col[u]] - pre;//子树v中以颜色col[u]为根的子树大小 szv = sz[v] - tmp; //子树v中其它颜色的连通的节点数量 pre = sum[col[u]]; ans -= szv * (szv - 1) / 2; c += tmp; //c为了避免重复计算sz[u]的大小 } sum[col[u]] += sz[u] - c; //sum[col[u]]加上以u为根的子树大小}int main() { int n, cas = 0; //freopen("in.txt", "r", stdin); while (~scanf("%d", &n)) { init(n); int cnt = 0; for (int i = 1; i <= n; i++) { scanf("%d", &col[i]); if (!vis[col[i]]) { vis[col[i]] = 1; cnt++; } } for (int i = 1, u, v; i < n; i++) { scanf("%d%d", &u, &v); add(u, v); add(v, u); } ans = (LL)cnt * n * (n - 1) / 2; dfs(1, -1); for (int i = 1; i <= n; i++) { if (sum[i] == 0) continue; LL tmp = n - sum[i]; ans -= tmp * (tmp - 1) / 2; } printf("Case #%d: %lld\n", ++cas, ans); } return 0;}
转载地址:http://mswpn.baihongyu.com/