矩陣樹 [2020 Multi-University Training Contest 6 Expectation]
阿新 • • 發佈:2020-08-07
矩陣樹 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; }