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,v≤n), 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;
}