洛谷 P4455 [CQOI2018]社交网络(矩阵树定理,求行列式)

题面

题意

给出一个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;
}

猜你喜欢

转载自blog.csdn.net/yzyyylx/article/details/79979241