1. 程式人生 > 實用技巧 >矩陣樹 [2020 Multi-University Training Contest 6 Expectation]

矩陣樹 [2020 Multi-University Training Contest 6 Expectation]

矩陣樹 2020 Multi-University Training Contest 6 Expectation

題目大意:

給你一張n個點m條邊的圖,定義一個最小生成樹的權值是所有邊相與的結果,現在你隨機選一個最小生成樹,計算期望的權值,答案對998244353取模。

題解:

分析題目:

  • 這個期望等於概率乘以權值,每一張圖出現的概率可以算出來,所以現在只要求所有可能的權值之和即可。
  • 求一張圖所有最小生成樹,一般用矩陣樹來寫,所以考慮怎麼建樹,達到題目的要求。
  • 這個題目要求最小生成樹的權值相與,那麼可以考慮把權值拆開成二進位制,1e9,最多32位,所以對於每一位,答案就是最小生成樹的個數乘以一個\(2^x\)
#include <bits/stdc++.h>
#define debug(x) cout<<"debug:"<<#x<<" = "<<x<<endl;
using namespace std;
typedef long long ll;
const int maxn = 110;
const int mod = 998244353;
ll K[maxn][maxn];
ll Gauss(int n){//求矩陣K的n-1階順序主子式
    ll res=1;
    for(int i=1;i<=n-1;i++){//列舉主對角線上第i個元素
        for(int j=i+1;j<=n-1;j++){//列舉剩下的行
            while(K[j][i]){//輾轉相除
                ll t=K[i][i]/K[j][i];
                for(int k=i;k<=n-1;k++)//轉為倒三角
                    K[i][k]=(K[i][k]-t*K[j][k]+mod)%mod;
                swap(K[i],K[j]);//交換i、j兩行
                res=-res;//取負
            }
        }
        res=(res*K[i][i])%mod;
    }
    return (res+mod)%mod;
}
struct node{
    int u,v;
    int bit[40];
}e[10010];

ll p[40];
void init() {
    p[0] = 1;
    for (int i = 1; i < 40; i++) p[i] = p[i - 1] * 2 % mod;
}
long long inv(long long x,long long mod) {
    x %= mod;
    long long k = mod - 2, ans = 1;
    while (k) {
        if (k & 1) ans = ans * x % mod;
        x = x * x % mod;
        k >>= 1;
    }
    return ans;
}
int main() {
    int T;
    init();
    scanf("%d", &T);
    while (T--) {
        int n, m;
        scanf("%d%d", &n, &m);
        memset(K, 0, sizeof(K));
        for (int i = 1; i <= m; i++) {
            int u, v, w;
            scanf("%d%d%d", &u, &v, &w);
            e[i].u = u, e[i].v = v;
            K[u][u]++, K[v][v]++;
            K[u][v]--, K[v][u]--;
            for (ll j = 32; j >= 0; j--) {
                int x = (1ll << j) & w;
                e[i].bit[j] = x;
            }
        }
        ll ans = 0;
        ll num = inv(Gauss(n), mod);
        for (ll i = 0; i <= 32; i++) {
            memset(K, 0, sizeof(K));
            for (int j = 1; j <= m; j++) {
                if (e[j].bit[i] == 0) continue;
                int u = e[j].u, v = e[j].v;
                if (u == v) continue;
                K[u][u]++, K[v][v]++;
                K[u][v]--, K[v][u]--;
            }
            ans = (ans + Gauss(n) * p[i] % mod) % mod;
        }
        ans = ans * num % mod;
        printf("%lld\n", ans);
    }
    return 0;
}