有向图(树形图)矩阵树定理 学习笔记+洛谷4455 bzoj5297 社交网络 +bzoj4894天赋

如果不知道矩阵树定理,请点击这里
有时我们做题会遇到一些看起来像是要用矩阵树,但是图却是有向图的题目。有人说,对于有向图来说,是没有生成树这个概念的,只有树形图的概念。顾名思义,树形图就是形状是树的有向图。对于这一类题目,分为两种情况,第一种是以 i 号点为起点的树形图的个数,这种称为外向树;第二种是以 i 号点为终点的树形图个数,这种称为内向树。
做题时一定认真读题,看明白要求的是哪一种树形图。
对于外向图,它构造出来的矩阵应该是出度矩阵-邻接矩阵,对于内向图,它构造出来的矩阵应该是入度矩阵-邻接矩阵。还有一点与矩阵树定理不太一样,就是题目让我们求第 i 个点为根的外向树或者内向树个数时,我们只能将构造出的矩阵的第 i 行和第 i 列去掉(也就是求 M i i ),而不是任意去掉一个 1 n 之间的编号相同的行和列。之后用像原来的矩阵树定理一样的方法求解即可。
注意扩欧求逆元容易在取模时出现错误,要特判一下原来的 a i i 的正负再确定是 ( x + m o d ) % m o d 还是 ( x m o d ) % m o d
另外就是注意取模!最近做了好多数据很大要多次取模的题,感觉这种卡取模的是真的烦,稍微出一点问题就一分不得,还有时候很长时间找不到错误所在,所以多积累点经验。
来两道例题:(都是裸题)
社交网络:

#include <bits/stdc++.h>
using namespace std;

int n,m,ji,mod=10007;
int a[300][300],ans=1;
void exgcd(int a,int b,int &x,int &y)
{
    if(!b)
    {
        x=1;
        y=0;
    }
    else
    {
        exgcd(b,a%b,y,x);
        y-=a/b*x;
    }
}
int ksm(int x,int y,int mod)
{
    int res=1;
    while(y)
    {
        if(y&1)
        res=(res*x)%mod;
        x=(x*x)%mod;
        y>>=1;
    }
    return res;
}
void gauss()
{
    for(int i=2;i<=n;++i)
    {
        int r=i;
        for(int j=i+1;j<=n;++j)
        {
            if(abs(a[r][i])<abs(a[j][i]))
            r=j;
        }
        if(abs(a[r][i])==0)
        {
            ans=0;
            return;
        }
        if(r!=i)
        {
            for(int j=2;j<=n;++j)
            swap(a[r][j],a[i][j]);
            ++ji;
        }
        ans=(ans*a[i][i])%mod;
        int x,y;
        exgcd((a[i][i]+mod)%mod,mod,x,y);
        x=(x%mod+mod)%mod;
        if(a[i][i]<0)
        x=(x-mod)%mod;
//        x=ksm(a[i][i],mod-2,mod);
        for(int j=i+1;j<=n;++j)
        {           
            int t=a[j][i]*x%mod;
            for(int k=2;k<=n;++k)
            a[j][k]=(a[j][k]-t*a[i][k])%mod;
        }
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;++i)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        --a[y][x];
        ++a[x][x];
    }
    for(int i=1;i<=n;++i)
    for(int j=1;j<=n;++j)
        a[i][j]=(a[i][j]+mod)%mod;
    gauss();
    if(ji%2)
    ans=-ans;
    ans=(ans+mod)%mod;
    printf("%d\n",ans);
    return 0;
}

天赋

#include <bits/stdc++.h>
using namespace std;

int n,ji;
char s[400][400];
long long a[320][320],mod=1000000007,ans=1;
long long ksm(long long x,long long y,long long mod)
{
    long long res=1;
    while(y)
    {
        if(y&1)
        res=(res*x)%mod;
        x=(x*x)%mod;
        y>>=1;
    }
    return res;
}
void gauss()
{
    for(int i=2;i<=n;++i)
    {
        int r=i;
        for(int j=i+1;j<=n;++j)
        {
            if(abs(a[r][i])<abs(a[j][i]))
            r=j;
        }
        if(abs(a[r][i])==0)
        {
            ans=0;
            return;
        }
        if(i!=r)
        {
            for(int j=2;j<=n;++j)
            swap(a[r][j],a[i][j]);
            ++ji;
        }
        ans=(ans*a[i][i])%mod;
        long long ji=ksm(a[i][i],mod-2,mod);
        for(int j=i+1;j<=n;++j)
        {
            long long t=a[j][i]*ji%mod;
            for(int k=2;k<=n;++k)
            a[j][k]=(a[j][k]-t*a[i][k]%mod+mod)%mod;
            //注意要+mod   
        }
    }
    if(ji%2)
    ans=mod-ans;//注意不能直接ans=-ans,之后再+mod,会WA 
    ans=(ans)%mod;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;++i)
    scanf("%s",s[i]+1);
    for(int i=1;i<=n;++i)
    for(int j=1;j<=n;++j)
    a[i][j]=s[i][j]-'0';    
    for(int i=1;i<=n;++i)
    for(int j=1;j<=n;++j)
    {
        if(i!=j)
        a[i][j]=-a[i][j];
    }
    for(int i=1;i<=n;++i)
    for(int j=1;j<=n;++j)
    {
        if(i!=j)
        a[i][i]-=a[j][i];       
    }
    for(int i=1;i<=n;++i)
    for(int j=1;j<=n;++j)
    a[i][j]=(a[i][j]+mod)%mod;
    gauss();
    printf("%lld\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/forever_shi/article/details/80657493
今日推荐