HDU-6832 A Very Easy Graph Problem【2020 Multi-University Training Contest 6】【最小生成树】【并查集】【dfs】

题目

HDU-6832

题意

一个无向图,给出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;
}
©️2020 CSDN 皮肤主题: 游动-白 设计师:上身试试 返回首页