1. 程式人生 > 實用技巧 >hdu 6832 A Very Easy Graph Problem 構造樹+dfs

hdu 6832 A Very Easy Graph Problem 構造樹+dfs

題意:

給你一個n個點m條邊的圖,對於第i條邊,它的長度是2i,對於每一個頂點,它不是0型別,就是1型別。你需要找出來對於所有的“兩個不同型別的點之間最短距離”的和

題解(參考:https://blog.csdn.net/wayne_lee_lwc/article/details/107851431):

因為20+21+22<23,即20+21+...+2n-1<2n

所以如果第i條邊連線的兩個點已經聯通,我們就不需要用這條邊。所以這裡用並查集判斷下

後面我們用

sum,以該節點為根的子樹中所有黑白點對的距離和
dp[0][0],子樹中所有黑節點到該節點的距離和
dp[0][1],子樹中黑節點的數量


dp[1][0],子樹中所有白節點到該節點的距離和
dp[1][1],子樹中白節點的數量
color,節點顏色

dfs先統計一下最小的子樹上上面個變數的值,然後再上升到更大的子樹上進行綜合統計

程式碼+註釋:

#include<stack>
#include<queue>
#include<map>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define fi first
#define se second
using
namespace std; typedef long long ll; const int N = 1e6 + 50; const long long mod = 1e9 + 7; struct Edge { ll point; ll next; long long w; } nxt[N]; struct Node { ll type; long long dp[2][2]; long long sum; } node[N]; ll fa[N]; ll head[N]; ll T,n,m,tot; ll finds(ll x) { if
(x!=fa[x]) { ll y=finds(fa[x]); return fa[x]=y; } return x; } long long ppow(ll p) { long long ans = 1; long long pow = 2; while(p) { if(p & 1) ans = (ans * pow) % mod; p >>= 1; pow = (pow * pow) % mod; } return ans; } void add_edge(ll x,ll y,long long w) { nxt[++tot] = {y,head[x],w}; head[x] = tot; } /* sum,以該節點為根的子樹中所有黑白點對的距離和 dp[0][0],子樹中所有黑節點到該節點的距離和 dp[0][1],子樹中黑節點的數量 dp[1][0],子樹中所有白節點到該節點的距離和 dp[1][1],子樹中白節點的數量 color,節點顏色 */ void dfs(ll k,ll f) { node[k].dp[node[k].type][1] = 1; for(ll i = head[k],j; i; i = nxt[i].next) { j = nxt[i].point; if(j == f) continue; dfs(j,k); node[k].dp[0][0] = (node[k].dp[0][0] + node[j].dp[0][0] + (node[j].dp[0][1] * nxt[i].w) % mod) % mod; node[k].dp[1][0] = (node[k].dp[1][0] + node[j].dp[1][0] + (node[j].dp[1][1] * nxt[i].w) % mod) % mod; node[k].dp[0][1] += node[j].dp[0][1]; node[k].dp[1][1] += node[j].dp[1][1]; node[k].sum = (node[k].sum + node[j].sum) % mod; } long long sum0 = node[k].dp[0][0]; long long cnt0 = node[k].dp[0][1]; long long sum1 = node[k].dp[1][0]; long long cnt1 = node[k].dp[1][1]; long long w; /* 比如k樹下面有i,j兩顆子樹,那麼i樹上的白色點到j樹上的黑色點的距離我們可以用: i樹上的白色點到k樹的距離加上j樹上黑色點到k樹的距離 */ for(ll i = head[k],j; i; i = nxt[i].next) { j = nxt[i].point; if(j == f) continue; w = nxt[i].w; //k樹上的黑色節點數量,與子樹j的白色節點,所有黑白隊的距離和 node[k].sum = (node[k].sum + ((cnt0 - node[j].dp[0][1]) * (node[j].dp[1][0] + w * node[j].dp[1][1])) % mod) % mod; //k樹上的白色節點數量,與子樹j的黑色節點,所有黑白隊的距離和 node[k].sum = (node[k].sum + ((cnt1 - node[j].dp[1][1]) * (node[j].dp[0][0] + w * node[j].dp[0][1])) % mod) % mod; } } int main() { cin >> T; while(T--) { tot = 1; scanf("%lld%lld",&n,&m); for(ll i = 1; i <= n; i++) { node[i] = {0,0,0,0,0,0}; head[i] = 0; scanf("%lld",&node[i].type); fa[i] = i; } for(ll i = 1; i <= m; i++) { ll x,y; scanf("%lld%lld",&x,&y); //加了下面這個判斷,那麼1就肯定是我們構造的樹的根節點 if(x>y) swap(x,y); if(finds(x) == finds(y)) continue; add_edge(x,y,ppow(i)); add_edge(y,x,ppow(i)); fa[fa[y]] = fa[x]; } dfs(1,0); cout << node[1].sum << endl; } }