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