hdu-6035 Colorful Tree

题目意思是计算所有路径(n*(n-1)/2)经过的不同颜色的数目和。

这个数目和可以转化为每种颜色经过的路径数目的求和,而这个求和又等价于颜色总数*n*(n-1)/2-没有经过某种颜色的边的数量的求和。

没有结果某种颜色的数量等价于由此颜色的节点将树拆分出多个联通块,每个联通块的节点数为k,则边数为k*(k-1)/2。

#include <iostream>
#include <vector>
#include <set>
#define LL long long
using namespace std;
int n;
const int N = 200050;
vector<int> g[N];
LL sum[N];//维护以某种颜色为根的子树和
int colors[N];
bool vis[N];
LL wayCnt;
void init()
{
    fill(vis, vis + n + 1, 0);
    fill(sum, sum + n + 1, 0);
    for (int i = 0; i <= n; i++) g[i].clear();
    wayCnt = 0;
}
LL dfs(int now)
{
    int col = colors[now];
    sum[col]++;
    LL preSum = sum[col];//用以计算当前点到某子树之间的联通块大小
    vis[now] = 1;
    LL nowTreeSize = 1;
    int fuck = 0;
    for (int i = 0; i < g[now].size(); i++)
    {
        int nx = g[now][i];
        if (vis[nx]) continue;
        LL siz=dfs(nx);
        LL change = sum[col] - preSum;//计算出底层增量
        LL blockSize = siz - change;
        //cout << now << ": " << blockSize << endl;
        sum[col] += blockSize;
        preSum = sum[col];
        fuck += blockSize;
        wayCnt += blockSize*(blockSize - 1) / 2;
        nowTreeSize += siz;
    }
    return nowTreeSize;
}
int main()
{
    cin.sync_with_stdio(false);
    int cas = 1;
    while (cin >> n)
    {
        set<int> cc;
        for (int i = 1; i <= n; i++)
            cin >> colors[i],cc.insert(colors[i]);
        init();
        for (int i = 0; i < n-1; i++)
        {
            int a, b;
            cin >> a >> b;
            g[a].push_back(b);
            g[b].push_back(a);
        }
        dfs(1);
        //cout << wayCnt << endl;
        for (int i = 1; i <=n; i++)
        {
            if (cc.find(i)!=cc.end()&&i != colors[1])
            {
                LL blockSize = n - sum[i];
                wayCnt += blockSize*(blockSize - 1) / 2;
            }
        }
        cout << "Case #" << cas++ << ": ";
        cout << cc.size()*n*(n-1)/2-wayCnt << endl;
    }
    return 0;
}
原文地址:https://www.cnblogs.com/LukeStepByStep/p/7270676.html