【2020杭电多校】第六场 Expectation 矩阵树定理
题意
给出一个 n 个点,m 条边的无向图,现在随机选择一颗生成树,一颗生成树的价值为边权的 & 和。
问生成树价值的期望是多少?
思路
按位考虑,枚举所有边,如果当前边权的二进制的第 i 位为 1 ,把这条边放到图中,然后使用矩阵树定理求生成树的数量。
答案\(rel = \sum_{i=0}^{31}num_i*(1<<i)*sum^{-1}\),sum表示整个图的生成树数量,\(num_i\)表示枚举第 i 位时生成树的数量
代码
#include <bits/stdc++.h>
#define fuck system("pause")
#define emplace_back push_back
#define pb push_back
using namespace std;
typedef long long ll;
const ll mod = 998244353;
const double eps = 1e-6;
const ll inf = 0x3f3f3f3f;
const ll N = 2e5 + 10;
ll mat[110][110];
struct Edge{
ll u, v, w;
} edge[N];
ll gauss(ll n){
ll ans = 1;
for (ll i = 1; i < n;i++){
for (ll j = i + 1; j < n; j++) {
while(mat[j][i]){
ll t = mat[i][i] / mat[j][i];
for (ll k = i; k < n;k++){
mat[i][k] = (mat[i][k] - t * mat[j][k] + mod) % mod;
}
swap(mat[i], mat[j]);
ans = -ans;
}
}
ans = (ans * mat[i][i]) % mod;
}
return (ans + mod) % mod;
}
ll qpow(ll a, ll b)
{
ll ans = 1;
a %= mod;
while (b) {
if (b & 1) {
ans = (ans * a) % mod;
}
b >>= 1;
a = a * a % mod;
}
return ans % mod;
}
int main(){
ll T;
scanf("%lld", &T);
while(T--){
ll n,m;
scanf("%lld%lld", &n, &m);
memset(mat, 0, sizeof(mat));
for (ll i = 1; i <= m;i++){
scanf("%lld%lld%lld", &edge[i].u, &edge[i].v, &edge[i].w);
}
ll ans = 0;
for (ll i = 0; i < 32;i++){
memset(mat, 0, sizeof(mat));
for (ll j = 1; j <= m;j++){
if(edge[j].w&(1<<i)){
ll u = edge[j].u, v = edge[j].v;
mat[u][u]++, mat[v][v]++;
mat[u][v]--, mat[v][u]--;
}
}
ll num = gauss(n);
ans = (ans + (1 << i) * num % mod) % mod;
}
memset(mat, 0, sizeof(mat));
for(ll i=1;i<=m;i++){
ll u = edge[i].u, v = edge[i].v;
mat[u][u]++, mat[v][v]++;
mat[u][v]--, mat[v][u]--;
}
printf("%lld\n", ans * qpow(gauss(n), mod - 2) % mod);
}
return 0;
}