题目链接
题目思路
给你一个n个点,m条无向边的图,每个点有是黑点或者白点,要你求所有黑点和所有白点的最短路的和。
题目思路
跑多次最短路显然会TLE,这个时候要注意他每条边的长度是 2 i 2^i 2i也就是说你跑了全部的前i条边的长度之和都比你跑第(i+1)条边的长度短,所以如果两个点能通过前i条边到达,那肯定比通过第i+1条更优,所以我们从1 到 m按顺序建最小生成树。对于白点和黑点的最短路,我们枚举每条边会被多少种白点和黑点通过,统计两侧的黑白点个数,计算贡献即可。
代码
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<cstdio>
#include<vector>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<unordered_map>
#define fi first
#define se second
#define debug printf(" I am here\n");
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
const ll INF=0x3f3f3f3f3f3f3f3f;
const int maxn=1e5+5,inf=0x3f3f3f3f,mod=1e9+7;
const double eps=1e-10;
int n,m,fa[maxn],head[maxn],cnt;
int a[maxn],sz0[maxn],sz1[maxn],sum0,sum1;
ll ans;
struct node{
int to,next,w;
}e[maxn<<1];
void init(){
sum0=sum1=ans=cnt=0;
for(int i=1;i<=n;i++){
fa[i]=i;
sz0[i]=sz1[i]=head[i]=0;
}
}
int findd(int x){
return x==fa[x]?x:fa[x]=findd(fa[x]);
}
void add(int u,int v,int w){
e[++cnt]={v,head[u],w};
head[u]=cnt;
}
void dfs(int son,int fa){
sz1[son]=(a[son]==1);
sz0[son]=(a[son]==0);
for(int i=head[son];i;i=e[i].next){
if(e[i].to==fa) continue;
dfs(e[i].to,son);
ans=(ans+1ll*e[i].w*(1ll*sz1[e[i].to]*(sum0-sz0[e[i].to])+1ll*sz0[e[i].to]*(sum1-sz1[e[i].to])))%mod;
sz1[son]+=sz1[e[i].to];
sz0[son]+=sz0[e[i].to];
}
}
signed main(){
int _;scanf("%d",&_);
while(_--){
scanf("%d%d",&n,&m);
init();
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
sum1+=(a[i]==1);
sum0+=(a[i]==0);
}
int len=1;
for(int i=1,u,v;i<=m;i++){
len=len*2%mod;
scanf("%d%d",&u,&v);
if(findd(u)==findd(v)) continue;
fa[findd(u)]=findd(v);//注意是合并祖宗
add(u,v,len),add(v,u,len);
}
dfs(1,1);
printf("%lld\n",ans);
}
return 0;
}