colorful tree

原题地址:http://acm.hdu.edu.cn/showproblem.php?pid=6035

题意解析:题目意思为给你一棵树,有n个节点,n-1条边,每个节点用颜色标记,颜色的标号为1~n,整棵树中的每条路的路径权重为路径路过的不同的节点的颜色总数,例如路径为1->2->3,对应的路径为1->2->1,那么这条路的权重为2,题目要求求出这棵树的所有路径的权重之和。(一棵树的所有路径条数为n*(n-1)/2)

解题思路:从这道题的题目中我们可以看到,从问题的正方面我们可能无从下手,然后,这是一道典型的逆向思维题目+树形DP题,首先,我们从逆向思维出发,想到对于每种相同颜色的节点求出通过这种颜色的路径的总条数,然后求出所有颜色的路径数之和即为正解,最后想到求出通过每种颜色的路径数等效于总边数(n*(n-1)/2)减去不通过这种颜色的路径数,而求出不通过这种颜色的总路径数也就等效于除去这种颜色后求连通块中每个节点的个数(不通过的总路径数等于每个连通块中节点个数×(节点个数-1)/ 2)。最后,我们总结出来这道题的模型,求一棵树中对于每种颜色去除它之后的每个连通块的节点个数。对于所有的颜色,我们设计基于DP的思想,将每种颜色的数据用数组保存起来,根据DFS回溯的方法,我们可以从叶子节点不断往上回溯,找到每种颜色的对应的连通块,从而达到一遍DFS搞定这道题目。

题目代码(打了注释哦!):

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long ll;
const int MAX=200005;
vector<int> G[MAX];
ll not_in[MAX];   //以节点编号为i的每个子节点为根节点的子树中和节点i颜色相同的最近子树节点数
ll not_in_root[MAX];    //颜色为i的离根节点最近的子树的节点数量
ll top[MAX];      //与当前节点颜色相同的上一节点编号
ll color[MAX];    //节点颜色存储数组
ll get_paths(ll n) {
    return n*(n-1)/2;
}
ll ans;           //保存答案变量
ll DFS(int a) {
    int now_top=top[color[a]];   //记录与当前节点颜色相同的上一节点的编号
    top[color[a]]=a;             //更新编号
    ll n_num=1;                  //用于记录当前节点为根节点的子树的节点数量
    int l=G[a].size();
    for (int i=0; i<l; i++) {
        not_in[a]=0;            //对于当前节点的每个子树,not_in数组清零,从而便于求出每棵子树对于当前节点的连通块中节点个数
        ll tmp=DFS(G[a][i]);
        ans-=get_paths(tmp-not_in[a]);   //当前连通块中的节点个数等于tmp-not_in[a]
        n_num+=tmp;
    }
    (now_top?not_in[now_top]:not_in_root[color[a]])+=n_num; //返回去时,加上和节点now_top颜色相同的最近子树节点数,对于最上面的一个节点,直接将其存入not_in_root中
    top[color[a]]=now_top; //回溯
    return n_num;
}
int main() {
    int cas=1;
    int n;
    int a,b;
    while (~scanf("%d", &n)) {
        for (int i=0; i<=n; i++) {
            not_in_root[i]=0;
            G[i].clear();
        }
        for (int i=1; i<=n; i++) {
            scanf("%lld", &color[i]);
        }
        for (int i=1; i<n; i++) {
            scanf("%d%d", &a, &b);
            if (a>b) swap(a,b);
            G[a].push_back(b);
        }
        ans=get_paths(n)*n; //注意,这里本来写的是n*n*(n-1)/2,但是对于int类型的n,这样直接相乘会超范围,将n转为long long类型才行
        DFS(1);
        for (int i=1; i<=n; i++) ans-=get_paths(n-not_in_root[i]);
        printf("Case #%d: %lld\n", cas, ans);
        cas++;
    }
    return 0;
}

 
 



猜你喜欢

转载自blog.csdn.net/xiaobai__lee/article/details/76671996