$HDU 2020$ 多校第六场

(6832 A Very Easy Graph Problem)

题意

给定一张 (n) 个顶点,(m) 条边的无向图,输入的第 (i) 条边的权值为 (2^i)

对于每一个顶点,标记为 (0)(1)

请计算

[sum_{i = 1}^{n}sum_{j = 1}^ndis(i, j) imes left[a_{i} = 1wedge a_{j} = 0 ight] ]

其中 (dis(i, j)) 表示 (i)(j) 的最短路

(left[a_{i} = 1wedge a_{j} = 0 ight])(Iverson bracket),即条件成立取值为 (1),不成立取值为 (0)

(1leq nleq 10^5, 1leq mleq 2 imes 10^5),对答案取模 (10^9 + 7)

题解

题意等价于,计算所有 (1) 点到所有 (0) 点的最短路之和

注意到边权的特殊性,即 (2^i > 2^1 + 2^2 + ... + 2^{i - 1})

设当前输入边 (i),端点为 (u, v)

  • (u, v) 已联通,那么必然有 (dis(u, v) < 2^i)
  • (u, v) 未联通,那么必然有 (dis(u, v) = 2^i)

所以考虑,对所有边按输入顺序构造 (MST),模拟 (Kruskal) 算法,得到一颗生成树

由前面的证明知道,这颗生成树的特殊之处在于

  • 是一颗最小生成树
  • 树上任意两点的距离一定是最短路径

下面在新的树上作树形 (dp),记录节点所在子树的 (0) 点和 (1) 点的数量,枚举树上每一条边计算贡献即可

#include <bits/stdc++.h>
#define fi first
#define se second
#define pii pair<int, int>
#define pb push_back
#define dbg(a, l, r) for(int i = l; i <= r; ++i) printf("%lld%c", a[i], " 
"[i == r])
typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const LL MOD = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const int DX[] = {0, -1, 0, 1, 0, -1, -1, 1, 1};
const int DY[] = {0, 0, 1, 0, -1, -1, 1, 1, -1};
const int N = 2e5 + 7;
const double PI = acos(-1);
const double EPS = 1e-6;
using namespace std;

int T, n, m, zero, one, a[N], pre[N], rk[N], dp[N][2];
vector<pii> g[N];
pii E[N];
void ini(int n) {for(int i = 1; i <= n; ++i) pre[i] = i, rk[i] = 1;}
int findPre(int x) {return pre[x] == x ? x : pre[x] = findPre(pre[x]);}
void uni(int x, int y)
{
    int fx = findPre(x), fy = findPre(y);
    if(fx == fy) return;
    if(rk[fx] < rk[fy]) pre[fx] = fy, rk[fy] += rk[fx];
    else pre[fy] = fx, rk[fx] += rk[fy];
}
bool commonpre(int x, int y) {int fx = findPre(x), fy = findPre(y); return fx == fy;}

void debug()
{
    for(int i = 1; i <= n; ++i) {
        cout << i << ":" << endl;
        for(auto it: g[i]) cout << it.fi << ' ';
        cout << endl;
    }
    for(int i = 1; i <= n; ++i) {
        cout << i << ":" << endl;
        cout << "0's num: " << dp[i][0] << endl;
        cout << "1's num: " << dp[i][1] << endl;
    }
}
void dfs(int u, int v, LL &ans)
{
    dp[u][a[u]]++;
    for(auto i: g[u]) {
        if(i.fi != v) {
            dfs(i.fi, u, ans);
            dp[u][0] += dp[i.fi][0];
            dp[u][1] += dp[i.fi][1];
        }
    }
    for(auto i: g[u]) {
        if(i.fi != v) {
            LL res1 = dp[i.fi][0] * (one - dp[i.fi][1]) % MOD * i.se % MOD;
            LL res2 = dp[i.fi][1] * (zero - dp[i.fi][0]) % MOD * i.se % MOD;
            ans = (ans + res1 + res2) % MOD;
        }
    }
}
int main()
{
    scanf("%d", &T);
    while(T--) {
        scanf("%d %d", &n, &m);
        memset(dp, 0, sizeof(dp));
        zero = one = 0;
        for(int i = 0; i <= n; ++i) g[i].clear();
        ini(n);
        for(int i = 1; i <= n; ++i) {
            scanf("%d", &a[i]);
            if(a[i] == 0) zero++;
            else one++;
        }
        for(int i = 1; i <= m; ++i) {
            int u, v;
            scanf("%d %d", &u, &v);
            E[i] = pii(u, v);
        }
        int cnt = 0;
        LL tp = 1, ans = 0;
        for(int i = 1; i <= m; ++i) {
            int x = E[i].fi, y = E[i].se;
            tp *= 2, tp %= MOD;
            if(!commonpre(x, y)) {
                uni(x, y);
                g[x].pb(pii(y, tp));
                g[y].pb(pii(x, tp));
                if(cnt == n - 1) break;
            }
        }
        dfs(1, -1, ans);
        //debug();
        printf("%lld
", ans);
    }
    return 0;
}
/*
3
3 2
0 1 0
3 1
3 2
3 2
1 1 0
3 1
3 2
4 4
1 0 1 0
1 2
2 3
3 4
1 4
*/
原文地址:https://www.cnblogs.com/ChenyangXu/p/13449595.html