hdu6832 图转树->树形dp算点权

这道题比赛的时候真的很可惜,就差一点点,当时脑子胡了,边权点权傻傻分不清

题意

有n个点 m条边的无向图,每个点分别是0 或者 1,问所有0到1的点的距离之和是多少,每条边输入的距离就是2^(i-2)(第一行输入n,m,第二行输入n个0or1,接下来输入m行,第i行输入就是2的i-2次)输出mod 1e9+7

思路

其实,比赛时候把他直接转换成最小生成树,因为边是按照从小到大排序输入,所以直接用并查集那种方法生成最小生成树。

然后计算这个节点的子节点有多少0和1,统计出来。

接着,计算节点和他的子节点这条边做得贡献,(节点0数量 * 子节点1数量+节点1数量 * 子节点0数量)*边权之和就是答案(比赛时候点权搞错了,一直写成边权,答案输出一直不对,直接自闭)

代码块

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<map>
#include<queue>
#include<algorithm>
#define mod 1000000007
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
const int N=1e5+10;
struct node{
    int next,to;ll w;
    node(){}
    node(int sx,int sy,ll ww):next(sx),to(sy),w(ww){}
}a[N<<2];
ll sum,dp[N][2],K;
int tot,head[N],n,m,f[N];
int b[N];
void add(int u,int v,ll w){
    a[tot].next=head[u];
    a[tot].to=v;
    a[tot].w=w;
    head[u]=tot++;
}
int F(int x){return f[x]==x?x:f[x]=F(f[x]);}
void dfs(int u,int fa){
    dp[u][1]=dp[u][0]=0;
    dp[u][b[u]]++;
    for(int i=head[u];i!=-1;i=a[i].next){
        int v=a[i].to;if(v==fa){continue;}
        dfs(v,u);
        dp[u][1]+=dp[v][1];
        dp[u][0]+=dp[v][0];
    }
    for(int i=head[u];i!=-1;i=a[i].next){
        int v=a[i].to;if(v==fa){continue;}
        sum+=((dp[v][0]*(K-dp[v][1]))%mod*a[i].w)%mod;sum%=mod;
        sum+=((dp[v][1]*(n-K-dp[v][0]))%mod*a[i].w)%mod;sum%=mod;
    }
}

int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        tot=0;K=0;
        for(int i=1;i<=n;i++){
            scanf("%d",&b[i]);if(b[i]==1){K++;}
            head[i]=-1;f[i]=i;
        }
        sum=0;
        ll zhi=1;
        for(int i=1;i<=m;i++){
            int u,v;
            scanf("%d%d",&u,&v);
            int uu=F(u),vv=F(v);zhi*=2;zhi%=mod;
            if(uu==vv){continue;}
            f[uu]=f[vv];
            add(u,v,zhi);add(v,u,zhi);

        }
        dfs(1,0);
        printf("%lld
",sum);
    }
    return 0;
}
/*
10
3 3
1 2 1
2 3 1
1 3 2
*/
原文地址:https://www.cnblogs.com/luoyugongxi/p/13571907.html