hdu6832 圖轉樹->樹形dp算點權
阿新 • • 發佈:2020-08-27
這道題比賽的時候真的很可惜,就差一點點,當時腦子胡了,邊權點權傻傻分不清
題意
有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 */