1. 程式人生 > 實用技巧 >hdu6832 圖轉樹->樹形dp算點權

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\n",sum);
    }
    return 0;
}
/*
10
3 3
1 2 1
2 3 1
1 3 2
*/