题面
题意
给出一个DAG,求以1为根的生成树的个数.
做法
这题的关键是要知道矩阵树定理,就是说对于一幅图,构造出其邻接矩阵和入度矩阵(对角线上为每个点的入度),并用入度矩阵减去邻接矩阵得到的矩阵的行列式即为答案.
矩阵的行列式就是对于所有种每行每列各选取一个元素的积的和,具体而言,对于三阶矩阵a,其行列式为:
a[1][1]*a[2][2]*a[3][3]+
a[1][1]*a[2][3]*a[3][2]+
a[1][2]*a[2][1]*a[3][3]+
a[1][2]*a[2][3]*a[3][1]+
a[1][3]*a[2][1]*a[3][2]+
a[1][3]*a[2][2]*a[3][1]
且行列式满足交换任意两行或两列,其行列值变为原来的相反数的性质,因此可以用高斯消元求解,因为高斯消元不改变原来矩阵的行列式(除了其中的换行操作),而且高斯消元后的矩阵为一个上三角矩阵(仅对角线与上,右边界组成的三角形内的数可能不为零),其行列式就变为了对角线上所有数的乘积.这样就能O(n^3)求矩阵的行列式了.
代码
#include<iostream>
#include<cstdio>
#define ll long long
#define N 300
#define M 10007
using namespace std;
ll n,m,a[N][N],ans=1;
inline ll ny(ll u)
{
ll v=M-2,res=1;
for(;v;)
{
if(v&1) res=res*u%M;
u=u*u%M;
v>>=1;
}
return res;
}
inline void xy()
{
ll i,j,k,tmp;
bool sw=0;
for(i=1;i<=n;i++)
{
for(j=i;j<=n;j++) if(a[j][i]) break;
if(i!=j) {for(k=i;k<=n;k++) swap(a[i][k],a[j][k]);sw^=1;}
for(j=i+1;j<=n;j++)
{
tmp=a[j][i]*ny(a[i][i]);
for(k=i;k<=n;k++)
{
a[j][k]=(a[j][k]-tmp*a[i][k]%M+M)%M;
}
}
}
for(i=1;i<=n;i++) ans=ans*a[i][i]%M;
if(sw&&ans) ans=M-ans;
}
int main()
{
ll i,j,p,q;
cin>>n>>m;
for(i=1;i<=m;i++)
{
scanf("%lld%lld",&p,&q);
a[q][p]--;
a[p][p]++;
}
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
a[i][j]=a[i][j+1];
}
}
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
a[i][j]=a[i+1][j];
}
}
n--;
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
a[i][j]=(a[i][j]+M)%M;
}
}
xy();
cout<<ans;
}