HDU-6035 Colorful Tree(树型dp)

Colorful Tree

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 512    Accepted Submission(s): 189


Problem Description
There is a tree with n nodes, each of which has a type of color represented by an integer, where the color of node i is ci .

The path between each two different nodes is unique, of which we define the value as the number of different colors appearing in it.

Calculate the sum of values of all paths on the tree that has n(n1)2 paths in total.
 

Input
The input contains multiple test cases.

For each test case, the first line contains one positive integers n , indicating the number of node. (2n200000)

Next line contains n integers where the i -th integer represents ci , the color of node i . (1cin)

Each of the next n1 lines contains two positive integers x,y (1x,yn,xy) , meaning an edge between node x and node y .

It is guaranteed that these edges form a tree.
 

Output
For each test case, output " Case # x : y " in one line (without quotes), where x indicates the case number starting from 1 and y denotes the answer of corresponding case.
 

Sample Input
  
  
3 1 2 1 1 2 2 3 6 1 2 1 3 2 1 1 2 1 3 2 4 2 5 3 6
 

Sample Output
  
  
Case #1: 6 Case #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]的点

#include<bits/stdc++.h>
using 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;
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值