2017第一次多校联合 hdu6035Colorful Tree

来源:互联网 发布:linux 显示当前目录 编辑:程序博客网 时间:2024/06/05 02:36

假装这里有链接

题意:给你一棵树,树上每个节点都有颜色,然后要统计每种颜色参与了多少路径。

题解:首先,这题是真的难。(菜~菜~菜~…….)有看到网上有dalao说这题真的不难。。。。

要统计每种颜色参与了多少路径,那就算没参与多少路径,然后用总的减去就好了。
sum[x]表示的遍历到当前位置,颜色为x的高度最高一批结点为根的子树大小总和。
具体思路借鉴了这篇博客:链接

#include<bits/stdc++.h>using namespace std;#define ll long longconst int maxn=2e5+9;ll color[maxn],sum[maxn],sz[maxn],vis[maxn];vector <int> tree[maxn];ll ans=0;ll dfs(int u,int b){    sz[u]=1;//sz表示树的大小    ll temp=0;    int l=tree[u].size();    for(int i=0;i<l;i++)    {        int v=tree[u][i];//v是子树        if(v==b)//b是父亲            continue;        ll last=sum[color[u]];//关键在这里,这里会标记进行递归之前的该种颜色的值        sz[u]+=dfs(v,u);        ll add=sum[color[u]]-last;        //add可以理解为以u为根的子树中以u的颜色为根的子树大小        ans+=(sz[v]-add)*(sz[v]-add-1)/2;        temp+=sz[v]-add;//temp就是不含u的颜色的联通块的节点数    }    sum[color[u]]+=temp+1;//加上不含u的颜色的联通块的节点数和根节点u本身,此时sum在数值上与sz相同    return sz[u];}int main(){    ll int n,ca=1,a,b;    while(~scanf("%d",&n))    {        int m=0;        memset(vis, 0, sizeof(vis));        memset(sum, 0, sizeof(sum));        for(int i=1; i<=n;i++)        {            scanf("%lld",&color[i]);            if(!vis[color[i]])            {                m++;            }            vis[color[i]]=1;            tree[i].clear();        }        for(int i=0; i<n-1; i++)        {            scanf("%d %d",&a,&b);            tree[a].push_back(b);            tree[b].push_back(a);        }        printf("Case #%d: ", ca++);        if(m==1)        {            printf("%lld\n",n*(n-1)/2);            continue;        }        ans=0;        dfs(1,-1);        for(int i=1;i<=n;i++)        {            if(!vis[i])                continue;            ans+=(n-sum[i])*(n-sum[i]-1)/2;//处理没有考虑的情况        }        printf("%lld\n",n*(n-1)/2*m-ans);    }}
原创粉丝点击