1. 程式人生 > 實用技巧 >HDU6821 A Very Easy Graph Problem(最小生成樹)

HDU6821 A Very Easy Graph Problem(最小生成樹)

題意:

給出一個無向連通圖,裡面的點分為0號點和1號點,第i條邊的邊權是2的i次。

詢問所有1號點到0號點的最短路徑之和。

題解:

如果對所有1號點跑dijkstra演算法,時間肯定是無法接受的。

觀察到題目的邊權有一個關鍵的性質,第i條邊權是2的i次,這說明前i-1條邊加起來都沒這條邊的邊權大。

猜想,所有最短路徑都在這個圖的最小生成樹上。

所以先建立最小生成樹,樹上的所有邊對答案都是有貢獻的,現在考慮單邊對答案的貢獻。我們可以用一遍DFS處理出每條邊左邊的1號點個數、右邊的0號點個數,兩者相乘再乘上這條邊的權值,左右相反同理,這樣就可以算出單邊對答案的貢獻。

#include<bits/stdc++.h>
using
namespace std; typedef long long ll; const int maxn=2e5+100; const int mod=1e9+7; const ll inf=1e18; typedef long long ll; struct node { int u,v; ll w; int nxt; }edge[maxn]; int head[maxn]; int tot; void addedge (int u,int v,int w) { edge[tot].u=u; edge[tot].v=v; edge[tot].w=w; edge[tot].nxt
=head[u]; head[u]=tot++; edge[tot].u=v; edge[tot].v=u; edge[tot].w=w; edge[tot].nxt=head[v]; head[v]=tot++; } ll w[maxn]; int a[maxn];//節點權值 int father[maxn]; int findfather (int x) { int a=x; while (x!=father[x]) x=father[x]; while (a!=father[a]) { int
z=a; a=father[a]; father[z]=x; } return x; } ll lst[maxn];//每個節點和他父親連的那條邊 int dfn[2][maxn]; int dfo[2][maxn]; int cnt[2]; int wjm[2][maxn]; void dfs (int u,int pre) { cnt[a[u]]++; dfn[0][u]=cnt[0]; dfn[1][u]=cnt[1]; for (int i=head[u];i!=-1;i=edge[i].nxt) { int v=edge[i].v; if (v==pre) continue; lst[v]=edge[i].w; dfs(v,u); } dfo[0][u]=cnt[0]; dfo[1][u]=cnt[1]; } int main () { w[0]=1; for (int i=1;i<maxn;i++) w[i]=(w[i-1]*2)%mod; int t,n,m; scanf("%d",&t); while (t--) { scanf("%d%d",&n,&m); for (int i=0;i<=n;i++) head[i]=-1; tot=0; int sum[2]; memset(sum,0,sizeof(sum)); cnt[0]=0; cnt[1]=0; for (int i=1;i<=n;i++) scanf("%d",a+i),father[i]=i,sum[a[i]]++,wjm[0][i]=0,wjm[1][i]=0,lst[i]=0; for (int i=1;i<=m;i++) { int u,v; scanf("%d%d",&u,&v); int faU=findfather(u); int faV=findfather(v); if (faU==faV) continue; father[faU]=faV; addedge(u,v,w[i]); } ll ans=0; dfs(1,0); for (int i=1;i<=n;i++) { //每個點與他父親連線的邊的貢獻 wjm[0][i]=dfo[0][i]-dfn[0][i]; wjm[1][i]=dfo[1][i]-dfn[1][i]; wjm[a[i]][i]++; ll x=wjm[0][i];//子樹裡0號點 ll y=sum[1]-wjm[1][i];//除子樹外的1號點 ll x1=wjm[1][i];//子樹裡1號點 ll y1=sum[0]-wjm[0][i];//除子樹外0號點 ans=ans+(lst[i]*x%mod)*y%mod+(lst[i]*x1%mod)*y1%mod; ans%=mod; } printf("%lld\n",ans); } }