BZOJ 5297: [Cqoi2018]社交网络 矩阵树定理

这里简单讲一下矩阵树定理:    

我们分 3 种情况:  

1. 给定一个无向图,求生成树个数.  

2. 给定一个有向图,求以一个点为根的内向树个数.  

3. 给定一个有向图,求以一个点为根的外向树个数.        

case1:

构造度数矩阵 $S1$,满足 $S1_{i,i}$ 等于 $i$ 点的度数(一条无向边当成两条有向边,且只有对角线有值)     

构造邻接矩阵 $S2$,满足 $S2_{i,j}$ 等于 $(i,j)$ 之间边的个数(一条无向边当成两条有向边)   

令矩阵 $d=S1-S2$,则生成树个数就等于 $d$ 去掉任意一行+一列后的行列式的值.   

case2:   

将 $1$ 中那个 $S1$ 矩阵中 $S1_{i,i}$ 变成 $i$ 点的出度.         

然后去掉矩阵 $d$ 中根节点所在的行与列.   

case3:   

将 $1$ 中那个 $S1$ 矩阵中 $S1_{i,i}$ 变成 $i$ 点的入度.         

然后去掉矩阵 $d$ 中根节点所在的行与列.   

code:

#include <cstdio> 
#include <algorithm> 
#define N 300    
#define mod 10007 
#define ll long long 
#define setIO(s) freopen(s".in","r",stdin)
using namespace std;
int a[N][N],n,m;            
int qpow(int x,int y) 
{
    int tmp=1; 
    for(;y;y>>=1,x=(ll)x*x%mod) 
        if(y&1) tmp=(ll)tmp*x%mod;    
    return tmp; 
}
int INV(int x) { return qpow(x,mod-2); } 
int gauss() 
{
    int ans=1,i,j,k;     
    for(i=2;i<=n;++i) 
    { 
        k=i; 
        for(j=i+1;j<=n;++j)  if(a[j][i]>a[k][i]) k=j;   
        if(k!=i) swap(a[i],a[k]),ans*=-1;    
        if(!a[i][i]) return 0;    
        int inv=INV(a[i][i]);        
        for(j=i+1;j<=n;++j) 
        {
            int t=(ll)(inv*a[j][i]%mod+mod)%mod;    
            for(k=i;k<=n;++k) a[j][k]=(ll)(a[j][k]%mod-(ll)t*a[i][k]%mod+mod)%mod;         
        }
    }
    if(ans<0) ans+=mod;     
    for(i=2;i<=n;++i) ans=(ll)(ans*a[i][i]%mod+mod)%mod;    
    return ans;   
}     
int main() 
{ 
    // setIO("input"); 
    int i,j; 
    scanf("%d%d",&n,&m);   
    for(i=1;i<=m;++i) 
    {
        int x,y; 
        scanf("%d%d",&x,&y); 
        // y->x 
        --a[y][x];   
        ++a[x][x];      
    }    
    printf("%d
",gauss());     
    return 0;
}

  

原文地址:https://www.cnblogs.com/guangheli/p/12256306.html