1. 程式人生 > 實用技巧 >A Very Easy Graph Problem HDU - 6832 (最小生成樹 + dfs)

A Very Easy Graph Problem HDU - 6832 (最小生成樹 + dfs)

Practice link :https://vjudge.net/problem/HDU-6832

題意: n 個點,m 條邊,第 i 條邊的權值是 2^i ,問每個 1 到每個 0 的最短距離之和。


思路:首先看邊的權值 是 2^i ,我們可以聯想到 2^0+2^1+......+2^(n-1)< 2^n,對於第 i 條邊,如果這條邊連線的 u和v 是已經被前 i - 1 條邊連線,則這條邊可以直接拋掉。看到這個選邊的過程,也就是最小生成樹kruskal發的選邊原則,因此我們可以跑一遍最小生成樹來確認整張圖,也就是一棵樹,在這個棵樹上每個01點對的路徑只有一條且保證是最短路徑。接下來我們只需要確定每一條邊所使用的次數即可。觀察一幅圖,對於任意一條邊,它的使用次數就是 這條邊上方的 1 的數量 * 下方 0 的數量 +上方的 0 的數量 * 下方的 1 的數量。

那麼接下來的問題就轉化為了如何得到這些數量,首先設這副圖 0 的數量為 blcak , 1 的數量為 white ,那麼只要儲存這條邊下方(也就是子樹)的 0 的數量 nb,1 的數量 nw,那麼答案就是 (black - nb)*nw + (white - nw)*nb,最後在乘以權值即可。那麼我們就可以用dfs去求出其子樹的 0 和 1 的數量。

程式碼:

  1 #include<bits/stdc++.h>
  2 #define ll long long
  3 #define MOD 1000000007 
  4 #define INF 0x3f3f3f3f3f
  5 #define mem(a,x) memset(a,x,sizeof(a))  
  6
#define _for(i,a,b) for(int i=a; i< b; i++) 7 #define _rep(i,a,b) for(int i=a; i<=b; i++) 8 #define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); 9 10 using namespace std; 11 const int MAXN = 200005 ; 12 inline int rd() 13 { 14 int res = 0,flag = 0; 15 char ch; 16 if
((ch = getchar()) == '-')flag = 1; 17 else if(ch >= '0' && ch <= '9')res = ch - '0'; 18 while ((ch = getchar()) >= '0' && ch <= '9')res = (res<<1) + (res<<3) + (ch - '0'); 19 return flag ? -res : res; 20 } 21 // 22 int head[MAXN]; 23 int num=0; 24 struct edg{ 25 int next,to; 26 ll w; 27 }edge[MAXN]; 28 void edge_add(int u,int v,int w) //鏈式前向星存圖 29 { 30 num++; 31 edge[num].next=head[u];edge[num].to=v;edge[num].w=w;head[u]=num; 32 edge[++num].next=head[v];edge[num].to=u;edge[num].w=w;head[v]=num; 33 } 34 int color[MAXN]; 35 vector<int>v; 36 struct node{ 37 int u,v; 38 ll w; 39 }s[MAXN]; 40 int fa[MAXN]; 41 int find(int x) 42 { 43 return (fa[x]==x)?x:(fa[x]=find(fa[x])); 44 } 45 void kruskal(int n,int m) 46 { 47 int nb=0; 48 for(int i=1;i<=m;i++){ 49 int fu=find(fa[s[i].u]),fv=find(fa[s[i].v]); 50 if(fu!=fv){ 51 fa[fu]=fv; 52 edge_add(s[i].u,s[i].v,s[i].w); 53 nb++; 54 v.push_back(s[i].w); 55 } 56 if(nb==n-1)break; 57 } 58 } 59 //////// 60 struct cc{ 61 int ww,bb; 62 }; 63 int mpw[MAXN]; 64 int mpb[MAXN]; 65 ll qpow(ll a,ll b) 66 { 67 ll ans=1; 68 while (b){ 69 if (b&1) ans=ans*a%MOD; 70 a=a*a%MOD; 71 b>>=1; 72 } 73 return ans; 74 } 75 cc dfs(int u,int fa) //求出任意邊以下的白點和黑點數 76 { 77 cc col; 78 col.ww=0,col.bb=0; 79 for(int i=head[u];i!=-1;i=edge[i].next) 80 { 81 int v=edge[i].to; 82 if(v==fa)continue; 83 cc now=dfs(v,u); 84 col.ww+=now.ww; 85 col.bb+=now.bb; 86 mpw[edge[i].w]=now.ww; //讀該邊以下的白點數 87 mpb[edge[i].w]=now.bb; 88 } 89 cc p=col; 90 if(color[u]==1){ 91 p.ww++; 92 return p; 93 }else{ 94 p.bb++; 95 return p; 96 } 97 } 98 int main() 99 { 100 int T,n,m; 101 scanf("%d",&T); 102 while(T--){ 103 num=0; 104 v.clear(); 105 mem(head,-1); 106 int white=0,black=0; 107 scanf("%d %d",&n,&m); 108 for(int i=1;i<=n;i++){ 109 scanf("%d",&color[i]); 110 if(color[i]==1){ 111 white++; 112 }else{ 113 black++; 114 } 115 mpb[i]=0; 116 mpw[i]=0; 117 fa[i]=i; 118 } 119 for(int i=1;i<=m;i++){ 120 scanf("%d %d",&s[i].u,&s[i].v); 121 s[i].w=i; 122 } 123 kruskal(n,m); 124 dfs(1,0); 125 ll ans=0; 126 for(auto i:v) 127 { 128 int nw=white-mpw[i]; 129 int nb=black-mpb[i]; 130 ans=(ans+(1ll*nw*mpb[i]%MOD)*(qpow(2,i)%MOD))%MOD; 131 ans=(ans+(1ll*nb*mpw[i]%MOD)*(qpow(2,i)%MOD))%MOD; 132 } 133 printf("%lld\n",ans); 134 } 135 return 0; 136 }