hdu传送门

题意

首先定义了一棵生成树的重量:这棵树所有边的按位与(AND)

给出一个无向连通图,随机挑选一个生成树,问这个生成树的重量的期望是多少。

矩阵树定理求的是

∑ T ∏ e ∈ T w e \sum\limits_{T}\prod_{e\in T}w_e TeTwe

期望就是总权值除以生成树的个数

个数可以让边权为一来求解

总权值不好搞,但是 a n d and and操作可以拆分为二进制来看

对于每一位二进制来说不是 1 1 1就是 0 0 0

这样就可以使用矩阵树定理来求解每一位的贡献

#include <bits/stdc++.h>
using namespace std;
const int maxn = 10009;
#define int long long
int a[109][109],n,m,l[maxn],r[maxn],w[maxn];
const int mod = 998244353;
int quick(int x,int n)
{
	int ans = 1;
	for( ; n ; n>>=1, x = x*x%mod)
		if( n&1 )	ans = ans*x%mod;
	return ans;
}
int det()
{
	int ans = 1;
	for(int i=2;i<=n;i++)
	{
		for(int j=i+1;j<=n;j++)
			if( !a[i][i]&&a[j][i] ){ swap(a[i],a[j]); ans = -ans; break;}
		int inv = quick( a[i][i],mod-2 );
		for(int j=i+1;j<=n;j++)
		{
			int temp = inv*a[j][i]%mod;
			for(int q=i;q<=n;q++)
				a[j][q] = ( a[j][q]-temp*a[i][q]%mod )%mod;
		}
	}
	for(int i=2;i<=n;i++)	ans = ans*a[i][i]%mod;
	return ( ans+mod )%mod;
}
signed main()
{
	int t;
	cin >> t;
	while( t-- )
	{
		cin >> n >> m;
		for(int i=1;i<=m;i++)
			cin >> l[i] >> r[i] >> w[i];
		int ans = 0;
		for(int i=0;i<=30;i++)
		{
			memset(a,0,sizeof(a));
			for(int j=1;j<=m;j++)
			{
				int x = l[j],y = r[j],val = (bool)((1<<i)&w[j]);
				a[x][x] = ( a[x][x]+val )%mod; a[y][y] = ( a[y][y]+val )%mod;
				a[x][y] = ( a[x][y]-val )%mod; a[y][x] = ( a[y][x]-val )%mod;
			}
			ans = ( ans+det()%mod*(1<<i)%mod )%mod;
		}
		memset( a,0,sizeof(a) );
		for(int i=1;i<=m;i++)
		{
			int x = l[i],y = r[i],val = 1;
			a[x][x] = ( a[x][x]+val )%mod; a[y][y] = ( a[y][y]+val )%mod;
			a[x][y] = ( a[x][y]-val )%mod; a[y][x] = ( a[y][x]-val )%mod;			
		}
		ans = ans*quick(det(),mod-2)%mod;
		ans = ( ans%mod+mod )%mod;
		printf("%lld\n",ans);
	}
}