HDU 6832 A Very Easy Graph Problem题解(最小生成树+思维)

题目链接

题目思路

给你一个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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值