hdu6836 Expectation(矩阵树定理,高斯消元)

题意:

给定n个点m条边的无向图,随机取出一棵生成树。
定义生成树的权值为生成树边权的位与&。
问取出的生成树权值期望。

数据范围:n<=100,m<=1e4

解法:

题解:
在这里插入图片描述

行列式:
交换两行/,行列式的值取反
将某行/列乘上k,行列式也乘上k
用一行的倍数减去另一行,行列式值不变
将n阶矩阵化为上三角矩阵(右上角),行列式的值就是对角线元素乘积
---

无向图矩阵树定理:(有向图的不挂了)

前提:
图允许重边,不允许自环

定义:
1.图G的度数矩阵D(G):
D[i][i]=de[i],D[i][j]=0,其中i!=j

2.图G的邻接矩阵A(G):
A[i][j]=e(i,j),其中e(i,j)表示i到j的边数,且i!=j

3.图G的Kirchhoff矩阵L(G):
L[i][j]=D[i][j]-A[i][j]

实际运用中构造L(G)矩阵的方法:
对于边(u,v):
L[u][u]++,L[v][v]++,L[u][v]--,L[v][u]--.
(L[x][x]表示x点的度数,L[x][y]表示x到y的边数取反(x!=y))

4.图G的生成树数量为t(G)

定理:
1.L(G)的所有n-1阶主子式都相等

2.t(G)=L(G)的n-1个非零特征值乘积
上三角矩阵的特征值就是对角线元素
n-1个非零特征值乘积其实就是任意找一个n-1阶主子式,化为上三角矩阵后计算对角线元素乘积,
(其实就是选择一个i,删掉第i行第i列,方便起见一般是删掉最后一行和最后一列)

---

注意事项:
行列式一定要是缩为上三角(右上角)
矩阵树定理需要删去最后一行和最后一列,n阶矩阵计算前n-1阶的行列式就行了
(删除第i行和第i列也可以,但是删最后一行和最后一列比较方便)

---

ps:
本题需要取模,而高斯消元中有除法,因此用了辗转相除法(欧几里得算法),
但是这题的模数是质数,似乎可以直接用逆元

code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define ll long long
typedef pair<int,int> PI;
const int maxm=1e5+5;
const int mod=998244353;
struct Node{
    int a,b,c;
}e[maxm];
int n,m;
int K[105][105];
int ppow(int a,int b,int mod){
    int ans=1%mod;a%=mod;
    while(b){
        if(b&1)ans=ans*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ans;
}
int guass(int n){
    int ans=1;
    for(int i=1;i<=n;i++){
        for(int j=i+1;j<=n;j++){
            while(K[j][i]){//直到为0
                int t=K[i][i]/K[j][i];//计算第i行对应的数是第j行的几倍
                for(int k=i;k<=n;k++){//一个一个消去并交换数字(消去之后之前的位置变小)
                    K[i][k]=(K[i][k]-t*K[j][k]%mod+mod)%mod;
                    swap(K[i][k],K[j][k]);//交换
                }
                ans=-ans;//交换行,行列式的值取反
            }
        }
        if(!K[i][i])return 0;//生成树数量为0
        ans=(ans*K[i][i]%mod+mod)%mod;//上三角行列式(右上角)的值为对角线乘积
    }
    return ans;
}
signed main(){
    int T;cin>>T;
    while(T--){
        cin>>n>>m;
        for(int i=1;i<=m;i++){
            cin>>e[i].a>>e[i].b>>e[i].c;
        }
        int ans=0;
        for(int k=0;k<=30;k++){
            memset(K,0,sizeof K);
            for(int i=1;i<=m;i++){
                int a=e[i].a,b=e[i].b,c=(e[i].c>>k&1);
                K[a][a]+=c;
                K[b][b]+=c;
                K[a][b]-=c;
                K[b][a]-=c;
            }
            ans=(ans+guass(n-1)*(1<<k)%mod)%mod;
        }
        memset(K,0,sizeof K);
        for(int i=1;i<=m;i++){
            int a=e[i].a,b=e[i].b;
            K[a][a]++;
            K[b][b]++;
            K[a][b]--;
            K[b][a]--;
        }
        int cnt=guass(n-1);
        ans=ans*ppow(cnt,mod-2,mod)%mod;
        ans=(ans%mod+mod)%mod;
        cout<<ans<<endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44178736/article/details/107849281