HDU6832 A Very Easy Graph Problem(最小生成树)

Problem Description

An undirected connected graph has n n nodes and m m edges, The i -th edge’s length is \(2^i\), Each node i has a value \(a_i\) , which is either 0 or 1. You need to calculate:

\(\Sigma^n_{i=1}\Sigma^n_{j=1}d(i,j)\times[a_i=1 ∧ a_j=0]\)

d(i,j) indicates the shortest distance between i and j . [ ] is the Iverson bracket. ∧ indicates AND. Because the answer may be too large, please output the answer modulo \(10^9+7\).

Input

The first line contains one integer T(1≤T≤8),indicating the number of test cases.

The second line contains two ingeters n,m(1≤n\(10^5\),1≤m\(2\times10^5\)).

The third line contains n positive integers a1,a2,...,\(a_n\)(\(a_i\)=0 or 1) —— the value of the nodes.

The following m lines contain two ingeters u,v(1≤u,vn), and the i-th line represents the i-th undirected edge’s length is 2i, between node u and v.

The sum of n,m is no more than 2×\(10^5\).

Output

Print a single integer—— the value of the answer modulo 109+7.

Sample Input

1

3 2

0 1 0

3 1

3 2

Sample Output

10

关键就是要注意到边权的特殊性...

题目要求所有白点和黑点最短路的值。
每条边的权值是\(2^i\),我们可以发现对于第\(i\)条边,\(2^1+2^2+...+2^{i-1}<2^i\),所以如果两个点能通过\(i-1\)条边到达,那肯定比通过第\(i\)条更优,所以我们从\(1\)\(n\)按顺序建最小生成树。

对于白点和黑点的最短路,我们枚举每条边会被多少种白点和黑点通过,统计两侧的黑白点个数,计算贡献即可。

看到有大佬写的点分治:https://blog.csdn.net/weixin_44282912/article/details/107849300

以及奇奇怪怪的树状数组维护:https://blog.csdn.net/leoxe/article/details/107844387

也是很巧妙的思路。

#include 
#define N 100005
#define M 200005
#define ll long long
using namespace std;
const ll mod = 1e9+7;
int n, m, tot = 0, tot0, tot1, head[N], ver[2 * M], Next[2 * M], fa[N], a[N];//tot0和tot1为总的0和1的个数 
int zero[N], size[N];//zero[i]表示i为根的子树里0的个数 (包括自己) 1的个数可以用size减出来 
long long ans = 0;
ll edge[2 * M];
void add(int x, int y, ll z)
{
	ver[++tot] = y, edge[tot] = z, Next[tot] = head[x], head[x] = tot;
}
int get(int x)
{
	if(x == fa[x]) return x;
	return (fa[x] = get(fa[x]));
}
int dfs0(int x, int pre)
{
	size[x] = 1;
	for(int i = head[x]; i; i = Next[i])
	{
		int y = ver[i];
		if(y == pre) continue;
		size[x] += dfs0(y, x);
	}
	return size[x];
}
int dfs1(int x, int pre)
{
	if(a[x] == 0) zero[x] = 1;
	else zero[x] = 0;
	for(int i = head[x]; i; i = Next[i])
	{
		int y = ver[i];
		if(y == pre) continue;
		zero[x] += dfs1(y, x);
	}
	return zero[x];
}
void dfs(int x, int pre)
{
	for(int i = head[x]; i; i = Next[i])
	{
		int y = ver[i];
		ll z = edge[i];
		if(y == pre) continue;
		int up0 = tot0 - zero[y];
		int up1 = tot1 - (size[y] - zero[y]);
		ans = (ans + z * (zero[y] * up1 % mod) % mod ) % mod;
		ans = (ans +  z * (size[y] - zero[y]) % mod * up0 % mod ) % mod;
		dfs(y, x);
	}
}
signed main()
{
	//freopen("1.in","r",stdin);
	//freopen("my.out","w",stdout);
	int t;
	cin >> t;
	while(t--)
	{
		tot = tot0 = tot1 = 0;
		memset(head, 0, sizeof(head));
		memset(zero, 0, sizeof(zero));
		memset(size, 0, sizeof(size));
	 	ans = 0;
		cin >> n >> m;
		for(int i = 1; i <= n; i++)
		{
			cin >> a[i];
			if(a[i] == 0) tot0++;
			else tot1++; 
			fa[i] = i;
		}
		ll val = 1;
		for(int i = 1; i <= m; i++)
		{
			int x, y;
			cin >> x >> y;
			val = val * 2 % mod;
			int xx = get(x), yy = get(y);
			if(xx == yy) continue;
			fa[xx] = yy;//!!
			add(x, y, val);
			add(y, x, val);
		}
		dfs0(1, 0);
		dfs1(1, 0);
		dfs(1, 0);
		cout << ans << endl;
	}
	return 0;
}

你可能感兴趣的:(HDU6832 A Very Easy Graph Problem(最小生成树))