HDU6053(Colorful Tree)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6035

每一条路径中颜色数的总和,就是经过每一种颜色的路径条数的总和,那么每一种颜色的贡献定义为经过这种颜色的路径的数量。

要计算经过一种颜色的路径数量,可以先计算不经过这种颜色的路径的数量,用 n*(n-1)/2 减去这个数量即为经过这种颜色的路径的数量。

删除同一种颜色所有的点之后,这棵树就被分成了几个联通块儿,所有连通块内部路径数量总和就是不经过这种颜色的路径数量,但是实现还是要用树来解决,dfs。

sum[x] 表示搜索到当前状态时,高度最高的一批颜色为 x 的节点所代表的子树的大小总和,sz[x] 表示根节点为 x 的子树的大小。在搜索节点 u 时,对于一个子节点 v ,在搜索节点 v 之前的 sum[color[u]] 与搜索 v 之后的 sum[color[u]] 的差即为子树 v 与最高一批颜色为 color[u] 的子树之间的那些节点构成的连通块的节点总数。有了节点数,路径数即为 节点数*(节点数-1)/ 2。

举个例子,如图: 
这里写图片描述 
对于结点1,我们想求出与1相邻的且不是黑色的结点组成的连通块大小,此时就要dfs结点1的两个儿子2和3,若我们处理出来结点2中,最高的一批黑色结点(4,8)的所构成子树的大小分别为2和1,那么我们拿2为根子树的大小5,减去2,减去1,就得到了结点2不包含黑色结点的连通块大小是5-2-1=2,也就是图中的2和5组成的连通块。同理对3,可以求得连通块大小是2({3,6})。 

#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;

typedef long long LL;

const int maxn = 2e5 + 10;

LL sum[maxn];
LL sz[maxn];
LL color[maxn];
bool used[maxn];
vector<int> tree[maxn];
LL ans;
int n;

LL dfs(int u, int fa){
    sz[u] = 1;
    LL allson = 0;
    for (int i=0; i<tree[u].size(); i++){
        int v = tree[u][i];
        if (v == fa) continue;
        LL last = sum[color[u]];
        sz[u] += dfs(v, u);
        LL add = sum[color[u]] - last;
        ans += (sz[v] - add) * (sz[v] - add - 1LL) / 2LL;
        allson += sz[v] - add;
    }
    sum[color[u]] += allson + 1;

    return sz[u];
}

int main(){
    int t = 0;

    while (~scanf("%d", &n)){
        t++;
        memset(used, false, sizeof(used));
        memset(sum, 0, sizeof(sum));

        int cnt = 0;
        for (int i=1; i<=n; i++){
            scanf("%I64d", &color[i]);
            if (!used[color[i]]) cnt++;
            used[color[i]] = true;
            tree[i].clear();
        }
        for (int i=0; i<n-1; i++){
            int u, v;
            scanf("%d%d", &u, &v);
            tree[u].push_back(v);
            tree[v].push_back(u);
        }

        ans = 0;
        dfs(1, -1);
        for (int i=1; i<=n; i++){
            if (!used[i]) continue;
            ans += ((LL)n - sum[i]) * ((LL)n - sum[i] - 1LL) / 2LL;
        }
        printf("Case #%d: %I64d\n", t, (LL)cnt*(LL)n*(LL)(n-1LL)/2LL - (LL)ans);
    }
}


猜你喜欢

转载自blog.csdn.net/qust1508060414/article/details/76360368