传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4011
思路:首先要脑补一个结论,不考虑新加的边,树的个数=π degree[i](i!=1),degree指入度
因为除了根节点,每个点各选一条入边,就可以组成一棵树。
现在有了这条边x->y,我们如果还用入度乘积统计方案,就有可能多计算一些不合法的方案
这些方案都包含了一个有新边的环,于是我们就要想办法减去这一部分。
我们先统计一个特定的环,不合法的环一定是由一条y->x的路径+这条边x->y构成的,那么我们可以把这个环当一个点,含这个环的不合法方案数就是
Σy->x的不同路径 π (i不在枚举的路径上) degree[i]
于是开始DP,设f[i]表示 Σy->i的不同路径 π (j不在枚举的路径上) degree[j]
那么i的方案数就是所有能一步到i的点j的方案数之和
因为i不在y->j的路径上,所以会多乘一个degree[i],除去即可
转移方程就是f[i]=(Σ(j能一步到i)f[j])/degree[i]
y==1时要特判,不然会除0
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int maxn=100010,maxm=400010,mod=1000000007;
typedef long long ll;
using namespace std;
int n,m,sx,sy,pre[maxm],now[maxn],son[maxm],tot,in[maxn],q[maxm+10],head,tail,deg[maxn];ll ans=1,f[maxn],inv[maxm];
void add(int a,int b){pre[++tot]=now[a],now[a]=tot,son[tot]=b,in[b]++,deg[b]++;}
ll getinv(int a){
int b=mod-2;ll res=1,j=a;
for (;b;b>>=1,j=j*j%mod) if (b&1) res=res*j%mod;
return res;
}
void topDP(){
for (int i=1;i<=n;i++) if (!deg[i]) q[++tail]=i;head=0;
f[sy]=ans;
while (head!=tail){
if (++head>maxm) head=1;
int x=q[head];f[x]=f[x]*inv[deg[x]]%mod;
for (int y=now[x];y;y=pre[y]){
f[son[y]]=(f[son[y]]+f[x])%mod;
if (!(--in[son[y]])){
if (++tail>maxm) tail=1;
q[tail]=son[y];
}
}
}
}
int main(){
scanf("%d%d%d%d",&n,&m,&sx,&sy);
for (int i=1;i<=m+1;i++) inv[i]=getinv(i);
for (int i=1,a,b;i<=m;i++) scanf("%d%d",&a,&b),add(a,b);
deg[sy]++;
for (int i=2;i<=n;i++) ans=ans*deg[i]%mod;
if (sy==1) return printf("%lld\n",ans),0;
topDP(),printf("%lld\n",(ans-f[sx]+mod)%mod);
return 0;
}