题面
题意
给出一个DAG,求以1为根的生成树的个数.
做法
这题的关键是要知道矩阵树定理,就是说对于一幅图,构造出其邻接矩阵和入度矩阵(对角线上为每个点的入度),并用入度矩阵减去邻接矩阵得到的矩阵的行列式即为答案.
矩阵的行列式就是对于所有种每行每列各选取一个元素的积再乘上-1的逆序对次数的和,具体而言,对于三阶矩阵a,其行列式为下面几项的和:
a
[
1
]
[
1
]
∗
a
[
2
]
[
2
]
∗
a
[
3
]
[
3
]
∗
(
−
1
)
[
a
[
1
]
[
1
]
>
a
[
2
]
[
2
]
]
+
[
a
[
1
]
[
1
]
>
a
[
3
]
[
3
]
]
+
[
a
[
2
]
[
2
]
>
a
[
3
]
[
3
]
]
a[1][1]*a[2][2]*a[3][3]*(-1)^{[a[1][1]>a[2][2]]+[a[1][1]>a[3][3]]+[a[2][2]>a[3][3]]}
a[1][1]∗a[2][2]∗a[3][3]∗(−1)[a[1][1]>a[2][2]]+[a[1][1]>a[3][3]]+[a[2][2]>a[3][3]]
a
[
1
]
[
1
]
∗
a
[
2
]
[
3
]
∗
a
[
3
]
[
2
]
∗
(
−
1
)
[
a
[
1
]
[
1
]
>
a
[
2
]
[
3
]
]
+
[
a
[
1
]
[
1
]
>
a
[
3
]
[
2
]
]
+
[
a
[
2
]
[
3
]
>
a
[
3
]
[
2
]
]
a[1][1]*a[2][3]*a[3][2]*(-1)^{[a[1][1]>a[2][3]]+[a[1][1]>a[3][2]]+[a[2][3]>a[3][2]]}
a[1][1]∗a[2][3]∗a[3][2]∗(−1)[a[1][1]>a[2][3]]+[a[1][1]>a[3][2]]+[a[2][3]>a[3][2]]
a
[
1
]
[
2
]
∗
a
[
2
]
[
1
]
∗
a
[
3
]
[
3
]
∗
(
−
1
)
[
a
[
1
]
[
2
]
>
a
[
2
]
[
1
]
]
+
[
a
[
1
]
[
2
]
>
a
[
3
]
[
3
]
]
+
[
a
[
2
]
[
1
]
>
a
[
3
]
[
3
]
]
a[1][2]*a[2][1]*a[3][3]*(-1)^{[a[1][2]>a[2][1]]+[a[1][2]>a[3][3]]+[a[2][1]>a[3][3]]}
a[1][2]∗a[2][1]∗a[3][3]∗(−1)[a[1][2]>a[2][1]]+[a[1][2]>a[3][3]]+[a[2][1]>a[3][3]]
a
[
1
]
[
2
]
∗
a
[
2
]
[
3
]
∗
a
[
3
]
[
1
]
∗
(
−
1
)
[
a
[
1
]
[
2
]
>
a
[
2
]
[
3
]
]
+
[
a
[
1
]
[
2
]
>
a
[
3
]
[
1
]
]
+
[
a
[
2
]
[
3
]
>
a
[
3
]
[
1
]
]
a[1][2]*a[2][3]*a[3][1]*(-1)^{[a[1][2]>a[2][3]]+[a[1][2]>a[3][1]]+[a[2][3]>a[3][1]]}
a[1][2]∗a[2][3]∗a[3][1]∗(−1)[a[1][2]>a[2][3]]+[a[1][2]>a[3][1]]+[a[2][3]>a[3][1]]
a
[
1
]
[
3
]
∗
a
[
2
]
[
1
]
∗
a
[
3
]
[
2
]
∗
(
−
1
)
[
a
[
1
]
[
3
]
>
a
[
2
]
[
1
]
]
+
[
a
[
1
]
[
3
]
>
a
[
3
]
[
2
]
]
+
[
a
[
2
]
[
1
]
>
a
[
3
]
[
2
]
]
a[1][3]*a[2][1]*a[3][2]*(-1)^{[a[1][3]>a[2][1]]+[a[1][3]>a[3][2]]+[a[2][1]>a[3][2]]}
a[1][3]∗a[2][1]∗a[3][2]∗(−1)[a[1][3]>a[2][1]]+[a[1][3]>a[3][2]]+[a[2][1]>a[3][2]]
a
[
1
]
[
3
]
∗
a
[
2
]
[
2
]
∗
a
[
3
]
[
1
]
∗
(
−
1
)
[
a
[
1
]
[
3
]
>
a
[
2
]
[
2
]
]
+
[
a
[
1
]
[
3
]
>
a
[
3
]
[
1
]
]
+
[
a
[
2
]
[
2
]
>
a
[
3
]
[
1
]
]
a[1][3]*a[2][2]*a[3][1]*(-1)^{[a[1][3]>a[2][2]]+[a[1][3]>a[3][1]]+[a[2][2]>a[3][1]]}
a[1][3]∗a[2][2]∗a[3][1]∗(−1)[a[1][3]>a[2][2]]+[a[1][3]>a[3][1]]+[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;
}