2020年HDU多校第六场 1010 Expectation(矩阵树)

2020年HDU多校第六场 1010 Expectation(矩阵树)

题意:给一个图,输出其随机生成树的权值的期望,其权值为树的所有边的与。

题解:要写这个题,需要知道一个知识点,矩阵树定理,它可以求出一个图内生成树的数量,放个板子。

#include<iostream>
using namespace std;
const int mod=1e9+7;
int n;
int f[207][207];
//f[i][i] 存i点的度数
//f[i][j] 存i点到j点的边数的相反数
//ans为图的生成树数量
ll gauss () {
    ll ans = 1 ;
    for (int i = 1; i <= n; i ++) {
        for (int j = i + 1; j <= n; j ++) {
            while (f[j][i]) {
                ll t = f[i][i] / f[j][i] ;
                for (int k = i; k < tot; k ++)
                    f[i][k] = (f[i][k] - t * f[j][k] + mod) % mod ;
                swap (f[i], f[j]) ;
                ans = -ans ;
            }
        }
        ans = (ans * f[i][i]) % mod ;
    }
    return (ans + mod) % mod ;
}

然后,我们可以先求出生成树的总数,毕竟要求期望要除总数,然后算分子部分,由题意我们要求的是所有边的按位与,所以:

第一,我们将31位数分开算贡献,每位之间的答案互不影响;

第二,若要某位产生贡献,必然要其生成树的所有边都要有那一位,所以没有那一位的边不加入图。

意思就是枚举每一位,每次都重新建一次图用矩阵数定理算一次答案。

#include<iostream>
#include<cstring>
using namespace std;
#define ll long long
const ll mod=998244353;
ll n,m,t,sum;
ll f[207][207];
ll u[40007],v[40007],w[40007];
ll gauss () {
    ll ans = 1 ;
    for (int i = 1; i < n; i ++) {
        for (int j = i + 1; j < n; j ++) {
            while (f[j][i]) {
                ll t = f[i][i] / f[j][i] ;
                for (int k = i; k < n; k ++)
                    f[i][k] = (f[i][k] - t * f[j][k] + mod) % mod ;
                swap (f[i], f[j]) ;
                ans = -ans ;
            }
        }
        ans = (ans * f[i][i]) % mod ;
    }
    return (ans + mod) % mod ;
}
long long int pow(long long int x,long long int n,long long int mod)
{
    long long int res=1;
	while(n>0)
	{
	   if(n%2==1)
	   {
	   	 res=res*x;
	   	 res=res%mod;
	   }
	   x=x*x;
	   x=x%mod;
	   n>>=1;
	}
	return res;
}
void add(int u,int v){
	f[u][u]++;f[v][v]++;
	f[u][v]--;f[v][u]--;
}
void init(){
	memset(f,0,sizeof(f));
}
int main(){
	scanf("%lld",&t);
	while(t--){
		init();
		scanf("%lld%lld",&n,&m);
		for(int i=1;i<=m;i++){
			cin>>u[i]>>v[i]>>w[i];
			add(u[i],v[i]);
		}
		sum=gauss();
		sum=pow(sum,mod-2,mod);
		ll aqours=0,er=1;
		for(int i=0;i<=30;i++){
			init();
			for(int i=1;i<=m;i++){
				if(w[i]&er){
					add(u[i],v[i]);
				}
			}
			aqours=(aqours+gauss()*er%mod*sum%mod)%mod;
			er*=2;
		}
		printf("%lld\n",aqours);
	}
}

posted @ 2020-08-06 23:00  ccsu_madoka  阅读(190)  评论(0编辑  收藏  举报