hdu 多校联赛 Colorful Tree

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
(由于hdu登不上了 只能从vjudge上赋值过来了 原题连接:http://acm.hdu.edu.cn/showproblem.php?pid=6035
比赛的时候这节没看懂题意 样例都解释不了 后来多方打听才真正呢个的理解题意 

题意

树上每个节点有一种颜色 ci(1<=ci<=n) 2<=n<=2105 ,每个点对的路径值为这个路径上的颜色种数,求树上所有路径(n(n1)/2条路径)的长度和

思路

直接计算每个颜色对答案的贡献


自己的代码就是这样写的,感觉自己的思路说出来不是很好理解 = =,大家看不懂的话可以看第二种思路。

首先如果树上每个点的颜色都不同的话,那么答案就是每个点经过路径的数量之和,可是在问题中有的点的颜色会相同,所以有的路径不能走。

考虑用dfs的方法解题,dfs中我们遇到的第一个点,因为之前没有遇到其它点,所以显然这个点对答案的贡献就是经过它的所有路径(各个子树大小相乘),接着往下dfs,如果其子树中节点的颜色与第一个点的颜色不同,那么自然再统计一次就好(各个子树大小相乘,其中子树包括其父节点),但是有可能这个节点的颜色和第一个节点的颜色相同,那么这个节点向上只有一个联通块可以走,但是向下仍然可以访问其所有子节点。所以此题的关键是维护好与一个节点颜色相同的父节点之间的联通块大小,这个联通块的定义大致就是,一个父节点的子树大小,减去所有与其颜色相同的子节点子树大小。一父节点下方的联通块大小首先是其子树的大小,然后遇到一个相同颜色的子树,就减去这个子树的大小即可。考虑到某一个颜色的第一个节点没有与其颜色相同的父节点,所以加一个虚根。

联通块
如图,点1是父节点,点2对应的父节点联通块就是粉色部分,点3对应的联通块是橙色部分(因为点4还没有访问,所以访问了这里没有关系)点4是蓝色部分

自己的统计方法是先算父节点联通块与自身代表子树的路径条数,然后算自身子树的路径条数。

计算出答案上限,减去非法值

很多网上的代码和标程都是这样写的。

首先,颜色不会超过 n 种,那么我们假设每条路径上都有 n 种颜色,共有 n(n1)/2 条路径,答案就为 ans=nn(n1)/2 。我们从中减去非法的,或者说重复计算的值即为答案。

如何统计重复计算的值呢?利用前面说的联通块,一个联通块里面是不会出现与这个父节点颜色相同的节点的,设某个联通块的大小为 siz ,遍历某个颜色的所有节点,这些节点对应的联通块内部的路径都不会出现这个颜色,所以答案要减去 siz(siz1) 。统计结束后答案就被计算出了。

标程用到了dfs序的方法来统计联通块的大小(用dfs序来判断节点之间的父子关系)。

具体代码实现部分会在代码里解释

ac代码:

#include <bits/stdc++.h>
using namespace std;
const int N = 4e5+100;
typedef long long ll;
int c[N],vis[N];//c[i]表示第i个点的颜色,vis[i]表示的是i这个颜色有没有出现过 用于标记点
vector<int> e[N];//创建容器组 用于存储图
ll sum[N],size[N];//sum[i]表示的是这个颜色i所能管理的点,size[i]表示的是以i为根的点的个数
ll ans;
void dfs(int x,int y)
{
    size[x]=1;//指的是x为根的树的点的个数 加上他本上则+1
    sum[c[x]]++;// sum是指 当前颜色的所能管理的点(包括自身的总个数) 那么每种颜色 最后都是一个接近根的值
    //到不了根的部分 就要在后面剔除
    ll pre=sum[c[x]];// 指从根到 x ,c[x] 管辖的点的总个数
    for(int i=0; i<e[x].size(); i++)
    {
        if(e[x][i]==y) continue;
        dfs(e[x][i],x);
        size[x]+=size[e[x][i]];// 以x为根的树的点的总个数,当前的这个点还要加上他的子数上的点
        ll count=size[e[x][i]]-(sum[c[x]]-pre);// 总的减去被管理的部分 剩下的都是自由的联通块 直接求路径后面剔除
        ans=ans+(1LL*count*(count-1))/2;
        sum[c[x]]+=count;// 加上这些被剔除的,相当于把这些与根节点颜色不一样的点标记成颜色一样的,这样递归往上返回的时候就不会重复计算这些点了
        pre=sum[c[x]];//更新这个颜色所能管理的所有的点
    }
}

int main()
{
    int n,cas=1;//测试样例case的数值
    while(scanf("%d",&n)!=EOF)
    {
        int num=0;
        ans=0;
        memset(sum,0,sizeof(sum));
        memset(vis,0,sizeof(vis));
        for(int i=1; i<=n; i++)
        {
            e[i].clear();//清空容器
            scanf("%d",&c[i]);//输入代表颜色的数字
            if(!vis[c[i]])
            {
                vis[c[i]]=1;//标记这个颜色有没有出现过
                num++;//不同的颜色数目(有多少种颜色)
            }
        }
        for(int i=1; i<n; i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);//输入路径的两点
            e[u].push_back(v);//通过容器组建立类似与图的联系
            e[v].push_back(u);
        }
        dfs(1,0);//深搜函数 从第一个点开始
        ll ANS = 1LL*num*((1LL)*n*(n-1))/2;
        // 未剔除无相关点的路径长度的总的路径长度 注意把num和n转为ll格式
        for(int i=1; i<=n; i++)//遍历一遍所有出现过的颜色
        {
            if(vis[i])
            {
                ll ct=n-sum[i];
                ans+=ct*(ct-1)/2;//求出所有点无贡献路径的和
            }
        }
        printf("Case #%d: %lld\n", cas++, ANS-ans);//总的结果即是总路径的和减去无贡献路径的和
    }
}



n ( n 1 ) / 2
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值