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

题意:

给出一个无向连通图,里面的点分为0号点和1号点,第i条边的边权是2的i次。

询问所有1号点到0号点的最短路径之和。

题解:

如果对所有1号点跑dijkstra算法,时间肯定是无法接受的。

观察到题目的边权有一个关键的性质,第i条边权是2的i次,这说明前i-1条边加起来都没这条边的边权大。

猜想,所有最短路径都在这个图的最小生成树上。

所以先建立最小生成树,树上的所有边对答案都是有贡献的,现在考虑单边对答案的贡献。我们可以用一遍DFS处理出每条边左边的1号点个数、右边的0号点个数,两者相乘再乘上这条边的权值,左右相反同理,这样就可以算出单边对答案的贡献。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+100;
const int mod=1e9+7;
const ll inf=1e18;
typedef long long ll;
struct node {
    int u,v;
    ll w;
    int nxt;
}edge[maxn];
int head[maxn];
int tot;
void addedge (int u,int v,int w) {
    edge[tot].u=u;
    edge[tot].v=v;
    edge[tot].w=w;
    edge[tot].nxt=head[u];
    head[u]=tot++;
    
    edge[tot].u=v;
    edge[tot].v=u;
    edge[tot].w=w;
    edge[tot].nxt=head[v];
    head[v]=tot++;
} 
ll w[maxn];
int a[maxn];//节点权值 
int father[maxn];
int findfather (int x) {
    int a=x;
    while (x!=father[x]) x=father[x];
    while (a!=father[a]) {
        int z=a;
        a=father[a];
        father[z]=x;
    }
    return x;
}
ll lst[maxn];//每个节点和他父亲连的那条边
int dfn[2][maxn];
int dfo[2][maxn];

int cnt[2];
int wjm[2][maxn];
void dfs (int u,int pre) {
    cnt[a[u]]++;
    dfn[0][u]=cnt[0];
    dfn[1][u]=cnt[1];
    for (int i=head[u];i!=-1;i=edge[i].nxt) {
        int v=edge[i].v;
        if (v==pre) continue;
        lst[v]=edge[i].w;
        dfs(v,u);
    }
    dfo[0][u]=cnt[0];
    dfo[1][u]=cnt[1];
} 

int main () {
    w[0]=1;
    for (int i=1;i<maxn;i++) w[i]=(w[i-1]*2)%mod;
    int t,n,m;
    scanf("%d",&t);
    while (t--) {
        scanf("%d%d",&n,&m);
        for (int i=0;i<=n;i++) head[i]=-1;
        tot=0;
        int sum[2];
        memset(sum,0,sizeof(sum));
        cnt[0]=0;
        cnt[1]=0;
        for (int i=1;i<=n;i++) scanf("%d",a+i),father[i]=i,sum[a[i]]++,wjm[0][i]=0,wjm[1][i]=0,lst[i]=0;
        for (int i=1;i<=m;i++) {
            int u,v;
            scanf("%d%d",&u,&v);
            int faU=findfather(u);
            int faV=findfather(v);
            if (faU==faV) continue;
            father[faU]=faV;
            addedge(u,v,w[i]);
        }
        ll ans=0;
        dfs(1,0);
        for (int i=1;i<=n;i++) {
            //每个点与他父亲连接的边的贡献
            wjm[0][i]=dfo[0][i]-dfn[0][i];
            wjm[1][i]=dfo[1][i]-dfn[1][i];
            wjm[a[i]][i]++;
            ll x=wjm[0][i];//子树里0号点
            ll y=sum[1]-wjm[1][i];//除子树外的1号点
            ll x1=wjm[1][i];//子树里1号点
            ll y1=sum[0]-wjm[0][i];//除子树外0号点
            
            ans=ans+(lst[i]*x%mod)*y%mod+(lst[i]*x1%mod)*y1%mod;
            ans%=mod;
        }
    
        printf("%lld
",ans);
    }
}
原文地址:https://www.cnblogs.com/zhanglichen/p/13448187.html