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; } }