题意:给一个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; }