HDU-6035 Colorful Tree(dfs序造树)

152 篇文章 0 订阅
36 篇文章 0 订阅

题意;

每条路径的权值是:路径上经过的不同颜色节点的个数。

询问各个点互达后的路径权值总和

思路: 看了小半天才看懂。。。

首先解释下官方题解的意思。

对于所求的问题,可以转化为每种颜色对于答案可以贡献多少次值(颜色i对于在所有路径中出现了几次)。

颜色i贡献次数=所有路径-路径中未出现颜色i的路径条数。

之后把所有的颜色i相加即可。

代码中;siz表示以当前节点为根,子树大小

      sum表示以颜色i为节点的,所有子树大小

每次在dfs时,因先遍历其子树,所以sum[col[u]] 有可能会增加,而与之前未增加时候(第一次到u) 的sum[col[u]]做差就是在v节点中,以颜色col[u]为根的子树的总大小。

tmp=siz[v]-sonu 就是在v子树中 除u颜色之外的节点个数。

ans在这时减去tmp的组合就是在此不通过col[u]节点的连通块中的边的条数。

之后在main函数时 ans减去的值是有可能某些节点在颜色i的父亲等较高位置,因此sum[i]无法统计到,这是上层的连通块数量也是颜色i无法贡献的,所以也应该减去!!

#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
vector< int >vec[maxn];
int siz[maxn];
int vis[maxn];
int col[maxn];
int sum[maxn];
typedef long long ll;
ll ans,n;

void dfs(int u,int fa)
{
    siz[u]=1;
    int pre=sum[col[u]];
    int add=0;
    for(int i=0;i<vec[u].size();i++)
    {
        int v=vec[u][i];
        if(v==fa)
            continue;
        dfs(v,u);
        siz[u]+=siz[v];
        ll sonu=sum[col[u]]-pre;//表示在子树v中,以颜色col[u]为根的子树总大小
        ll tmp=siz[v]-sonu;     //表示在子树v中,除了u颜色之外的节点个数
        add+=sonu;              //add记录当前所有子树中以col[u]为根的节点总数
        ans-=tmp*(tmp-1)/2;     //表示v子树中,各个节点互相到达,无需进过col[u]
        pre=sum[col[u]];        //表示记录上一次的sum
    }
    sum[col[u]]+=siz[u]-add;//sum[col[u]]加上以u为根,属于这个节点的连通块的节点总数
}
int main()
{
    int cs=1;
    while(~scanf("%lld",&n))
    {
        memset(sum,0,sizeof(sum));
        memset(col,0,sizeof(col));
        memset(siz,0,sizeof(siz));
        memset(vis,0,sizeof(vis));
        int cnt=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&col[i]);
            if(!vis[col[i]])
                vis[col[i]]=1,cnt++;
        }
        int u,v;
        for(int i=1;i<=n;i++)
            vec[i].clear();
        for(int i=1;i<n;i++)
        {
            scanf("%d%d",&u,&v);
            vec[u].push_back(v);
            vec[v].push_back(u);
        }
        ans=cnt*(n)*(n-1)/2;
        dfs(1,0);
        for(int i=1;i<=n;i++)
        {
            if(!sum[i])
                continue;
            ll res=n-sum[i];
            ans-=(res-1)*res/2;//此步骤减去的是 在i颜色节点的 上面部分,有n-sum[i]个节点,这些节点互通不需要经过i颜色节点。
        }
        printf("Case #%d: %lld\n", cs++, ans);
    }
}
/*
2
1 2
1 2
*/


评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值