窝不会做啊QAQ。。看了题解发现一个DAG每个点选一条入边就是一颗有根树(森林),所以DAG的生成有根树的数量就是所有入度不为0的点的入度积。那么在DAG上加了一条边之后还是这样算就可能会有一些不合法的情况(成环)了,假设加进来的是s->t的边,那么环中一定包含了一条t->s的路径,所以我们的任务就是找出原DAG上包含t->s的路径的生成有根树的数量然后减掉就行了。。如果有t->x这条边,那么考虑原DAG上包含x->s的路径的生成有根树的数量这样一个子问题,假设这个值是f[x]已知,那么转移到t的时候实际上就是x的入度只能选t->x这条边了,所有要用f[x]/x的入度,再累加到t上。这样就找出了拓扑图DP的转移,然后从t开始dp即可,初始f[s]=原DAG入度乘积,求出f[t]之后最后答案就是DAG上加了一条边之后的入度乘积-f[t]/t的入度。
#include<iostream>
#include<cstdio>
#define ll long long
#define N 100005
#define M 200005
#define p 1000000007
using namespace std;
struct edge{
int e,next;
} ed[M];
int n,m,X,y,i,s,e,ne=0,a[N],in[N],u[N];
ll ans,f[N];
ll pow(ll a,int q)
{
if (q==0) return 1ll;
ll b=pow(a,q/2);
b=(b*b)%p;
if (q%2) b=(b*a)%p;
return b;
}
void add(int s,int e)
{
ed[++ne].e=e;ed[ne].next=a[s];a[s]=ne;
}
void dp(int x)
{
u[x]=1;
if (x==X)f[x]=ans;else f[x]=0;
for (int j=a[x];j;j=ed[j].next)
{
if (!u[ed[j].e]) dp(ed[j].e);
f[x]=(f[x]+(f[ed[j].e]*pow((ll)in[ed[j].e],p-2))%p)%p;
}
}
int main()
{
// freopen("hn.in","r",stdin);
scanf("%d%d%d%d",&n,&m,&X,&y);
for (i=1;i<=n;i++) a[i]=in[i]=u[i]=0;
for (i=1;i<=m;i++)
{
scanf("%d%d",&s,&e);
add(s,e);
in[e]++;
}
ans=1;
if (X==y||y==1)
{
for (i=2;i<=n;i++) ans=(ans*in[i])%p;
printf("%lld\n",ans);
return 0;
}
in[y]++;
for (i=2;i<=n;i++) ans=(ans*in[i])%p;
dp(y);
ans=(ans-(f[y]*pow((ll)in[y],p-2))%p+p)%p;
printf("%lld\n",ans);
}