题目
题意
一个无向图,给出n个点,依次给出m条边e[i],边的权值为2^i,每个点带权值1或者0,求下面式子的值:
d(i,j)为点i到j的最短路的值
1<=n<=1e5
1<=m<=2e5
题解
首先这个图上的任意两点的最短路的边是在这个图的最小生成树,并且边权是依次增大的,我们用并查集构建出最小生成树;
然后遍历每条边计算每条的贡献,通过这条边两侧的1和0的数量相乘再乘上边权就是该边的贡献;
计算两侧的不同点的数量就直接通过dfs得到
代码
/*
* @author: arc
* @date: 2020-08-06 19:34:17
*/
#include<cstdio>
#include<vector>
using namespace std;
typedef long long ll,LL;
#define mod 1000000007
const int maxn = 1e5+10;
int r[maxn];
int a[maxn];
vector<pair<int, ll> > G[maxn];
int cnt[maxn][2];
ll zero, one;
ll ans = 0;
void init(int n){
for(int i = 1; i <= maxn-5; i++){
r[i] = i;
G[i].clear();
cnt[i][0] = cnt[i][1] = 0;
}
}
int __find(int k){
if(r[k] == k) return k;
return r[k] = __find(r[k]);
}
void __merge(int a, int b){
int fa = __find(a);
int fb = __find(b);
if(fa != fb) r[fb] = fa;
}
ll dfs(int u, int f){
cnt[u][0] = cnt[u][1] = 0;
cnt[u][a[u]]++;
for(auto it: G[u]){
int v = it.first;
if(v == f)
continue;
dfs(v, u);
cnt[u][0] += cnt[v][0];
cnt[u][1] += cnt[v][1];
}
for(auto it: G[u]){
int v = it.first;
if (v == f) continue;
ans = (ans + 1LL * cnt[v][1] * (zero - cnt[v][0]) % mod * it.second) % mod;
ans = (ans + 1LL * cnt[v][0] * (one - cnt[v][1]) % mod * it.second) % mod;
}
return ans;
}
int main(){
#ifdef ONLINE_JUDGE
#else
freopen("temp.in","r",stdin);
freopen("temp.out","w",stdout);
#endif
int cas;
scanf("%d", &cas);
while(cas--){
int n, m;
scanf("%d%d", &n, &m);
init(n);
zero = one = 0;
for (int i = 1; i <= n; i++){
scanf("%d", &a[i]);
if(a[i] == 0){
zero++;
} else {
one++;
}
}
int u, v;
ll val = 1;
for (int i = 1; i <= m; i++){
scanf("%d%d", &u, &v);
val *= 2;
val %= mod;
if(__find(u) != __find(v)){
__merge(u, v);
G[u].push_back(make_pair(v, val));
G[v].push_back(make_pair(u, val));
}
}
ans = 0;
dfs(1, -1);
printf("%lld\n", ans);
}
return 0;
}