Expectation
题目传送门
题目大意
给你一个无向图,有n个点,m条边,每个边的边权为
w
i
w_i
wi ,定义树的权为树的所有边的边权的按位与。
现在我们随机选择该图的一个生成树,问其生成树的权期望是多少。
思路
先看一下官方题解
很明显的矩阵树,因为是按位与计算,每一个位相互独立,所以对于每一位都需要建立一个其含该位的边的基尔霍夫矩阵,求得该位下可以得到的生成树的个数,按照题解中,每位对答案的贡献即为
a
n
s
∗
2
i
s
u
m
ans*\frac{2^i}{sum}
ans∗sum2i,其中ans为当前位的生成树的个数。
基尔霍夫矩阵树定理:Matrix-Tree 定理
AC Code
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<vector>
#include<cstring>
using namespace std;
#define INF 0x3f3f3f3f
#define int long long
// #define TDS_ACM_LOCAL
const int N=209;
const int mod=998244353;
vector<int>mp[N][N];
int mat[N][N];
int n, m;
int quick_pow(int a, int b)
{
int res = 1;
while (b)
{
if (b & 1)
res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
int gauss(int n, int K[][N]){//求矩阵K的n-1阶顺序主子式
int res=1;
for(int i=1;i<=n-1;i++){
for(int j=i+1;j<=n-1;j++){
while(K[j][i]){
int 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]);
res=-res;
}
}
res=(res*K[i][i])%mod;
}
return (res+mod)%mod;
}
void solve(){
cin>>n>>m;
memset(mat, 0, sizeof(mat));
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
mp[i][j].clear();
int u, v, w;
for(int i=1; i<=m; i++){
cin>>u>>v>>w;
mp[u][v].push_back(w), mp[v][u].push_back(w);
//建立总的基尔霍夫矩阵
mat[u][v]--, mat[v][u]--;
mat[u][u]++, mat[v][v]++;
}
int sum=quick_pow(gauss(n, mat), mod-2); //求出总的生成树的个数的逆元
int f=0;
for(int k=0; k<=30; k++){
memset(mat, 0, sizeof(mat));
for(int i=1; i<=n; i++)
for(int j=i+1; j<=n; j++)
for(auto x:mp[i][j])
//求得每一位的基尔霍夫矩阵
if(x&(1<<k)){
mat[i][j]--, mat[j][i]--;
mat[i][i]++, mat[j][j]++;
}
f=(f+quick_pow(2,k)*gauss(n,mat)%mod)%mod; //求得每一位的生成树的个数
}
cout<<sum*f%mod<<endl;
return ;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
#ifdef TDS_ACM_LOCAL
freopen("D:\\VS code\\.vscode\\testall\\in.txt", "r", stdin);
freopen("D:\\VS code\\.vscode\\testall\\out.txt", "w", stdout);
#endif
int T;
cin>>T;
while(T--) solve();
return 0;
}