洛谷4455 [CQOI2018]社交网络 (有向图矩阵树定理)(学习笔记)
阅读原文时间:2023年07月09日阅读:2

sro_ptx_orz

qwq算是一个套路的记录

对于一个有向图来说

如果你要求一个外向生成树的话,那么如果存在一个\(u\rightarrow v\)的边

那么\(a[u][v]--,a[v][v]++\)

对应的去掉第\(i\)行和第\(i\)列的余子式,就是以\(i\)为根的生成树个数。

内向生成树也是同理。所有的反过来即可

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define mk make_pair
#define ll long long
//#define int long long
#define rint register int
using namespace std;
inline int read()
{
  int x=0,f=1;char ch=getchar();
  while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
  while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
  return x*f;
}
const int maxn = 310;
const int mod = 10007;
int qsm(int i,int j)
{
    int ans=1;
    while (j)
    {
        if (j&1) ans=ans*i%mod;
        i=i*i%mod;
        j>>=1;
    }
    return ans;
}
int a[maxn][maxn];
int n,m;
int d[maxn][maxn];
int b[maxn][maxn];
void gauss()
{
    int k=1;
    int ff=1;
    int ans=1;
    for (rint i=1;i<=n;++i)
    {
        int now = k;
        while (now<=n && !(a[now][i])) ++now;
        if (now==n+1) continue;
        if (now!=k) ff*=-1;
        for (rint j=1;j<=n;++j) swap(a[now][j],a[k][j]);
        int inv = qsm(a[k][i],mod-2);
        for (rint j=i+1;j<=n;++j)
        {
            int t = a[j][i]*inv%mod;
            for (rint p=1;p<=n;++p) a[j][p]=(a[j][p]-a[k][p]*t%mod+mod)%mod;
        }
        ++k;
    }
    for (rint i=1;i<=n;++i) ans=ans*a[i][i]%mod;
    if(ff==-1) cout<<mod-ans;
    else cout<<ans;
}
signed main()
{
  n=read(),m=read();
  for (rint i=1;i<=m;++i)
  {
      int x=read(),y=read();
      a[x][x]++;
      a[y][x]--;
  }
  for (rint i=1;i<=n;++i)
    for (rint j=1;j<=n;++j) b[i][j]=a[i][j];
  for (rint i=1;i<n;++i)
    for (rint j=1;j<n;++j)
    {
        a[i][j]=b[i+1][j+1];
    }
  n--;
  gauss();
  return 0;
}