题意:给一个DAG,然后多加一条边,求在这个图中有多少种不同的生成树;
解法:树上DAG+组合数;
1.树上DAG:因为在DAG上多加了一条边,所以原图会出现环,那么多算的答案就是 rel = ans / ( 环上的点的入度的累乘 ),最后的答案就是 ans-rel(要注意,多加了一条边后,图上可能不止一个环,此时的 rel就是∑tot,tot = ans / ( 环上的点的入度的累乘 ));我们可以在计算这个多算出来的贡献时使用 DP,而在 DP时可以在原图上进行,因为我们的结束边界是多加的那一条边的 end;
2.组合数:当图还是一个DAG时,由乘法原理可得 ans就是所有入度的乘积(这其实是一个定理——朱刘定理);
附上代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#define ll long long
using namespace std;
const int N = 2e5+10;
const int mod = 1e9+7;
inline int read(){
int ref=0,x=1;char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) ref=ref*10+ch-'0',ch=getchar();
return ref*x;
}
int n,m,xx,yy;
struct edge{
int next,to;
}a[N<<4];
int cnt,head[N<<4],x[N],y[N];
int ru[N],vis[N],f[N];
ll ans=1,sum=1;
void add(int u,int v){
a[++cnt]=(edge){head[u],v};
head[u]=cnt;
}
ll ksm(ll a,ll b){
ll rel=1;
a%=mod;
while(b){
if(b&1) rel=(rel*a)%mod;
a=(a*a)%mod;
b>>=1;
}
return rel;
}
ll inv(ll a){ return ksm(a,mod-2); }
void dfs(int x){
if(vis[x]) return ;
vis[x]=1;
if(x==yy){
f[x]=(sum*inv(ru[x]))%mod;
return ;
}
for(int i=head[x];i;i=a[i].next){
int v=a[i].to;
dfs(v);
f[x]=(f[x]+f[v])%mod;//乘法具有分配律,不影响最终的答案
}
f[x]=(f[x]*inv(ru[x]))%mod;
}
int main()
{
n=read(),m=read(),xx=read(),yy=read();
ru[1]++;
for(int i=1;i<=m;++i){
int x,y;
x=read(),y=read();
add(y,x);
ru[y]++;
}
for(int i=1;i<=n;++i){
if(i==yy) ans=(ans*(ru[i]+1))%mod;
else ans=(ans*ru[i])%mod;
sum=(sum*ru[i])%mod;
}
dfs(xx);
if(xx==yy||yy==1) printf("%lld",sum%mod);
else printf("%lld",(ans-f[xx]+mod)%mod);
return 0;
}