题意: 给你一个DAG,再给你加一条边,之后图不保证是DAG了,求新图中以一为根的生成树有多少个。
由于原图是个DAG,我们对每个点随机找爹,总能形成一棵生成树,那么答案就是2到n所有点入度的乘积。在加了一条边 x->y 之后,如果再套用这个公式,无非就是多了加了这条边之后与一条y->x的路径构成环的方案。
那么我们可以对于每一条y->x的路径考虑,它多贡献的答案就是除了它以外其他点的入度之积,可以看作又分成了若干个DAG。
这样的话思路就挺明显了,令dp[i]表示从y出发走到i点对答案多产生的贡献,令ans为2号点到n号点的乘积,初始dp[y]设置为ans/deg[y],按照图的拓扑序进行转移,走到每一个新点就相当于把这个点加入从y出发的路径,即
dp[nex]+=dp[now]/deg[nex]
这样求得的dp[x]即为答案。
下附AC代码。
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define maxn 200005
using namespace std;
typedef long long ll;
const int mod=1e9+7;
int n,m,nx,ny,tot,ans=1;
int head[maxn],nex[maxn],to[maxn];
void add(int x,int y)
{
to[++tot]=y;nex[tot]=head[x];head[x]=tot;
}
int deg[maxn],deg1[maxn],inv[maxn];
void getinv()
{
inv[1]=1;
for(int i=2;i<=m+1;i++)
{
inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
}
}
int q[maxn],dp[maxn];
void topologicalsort()
{
int h=0,t=-1;
dp[ny]=ans;
// cout<<"its "<<ny<<" "<<ans<<endl;
for(int i=1;i<=n;i++)
if(deg[i]==0)
q[++t]=i;
while(h<=t)
{
int now=q[h]; h++;
// cout<<now<<" "<<dp[now]<<endl;
dp[now]=(1ll*dp[now]*inv[deg1[now]])%mod;
// cout<<now<<" "<<dp[now]<<" "<<inv[deg1[now]]<<endl;
for(int i=head[now];i;i=nex[i])
{
dp[to[i]]=(dp[to[i]]+dp[now])%mod;
deg[to[i]]--;
if(deg[to[i]]==0)
q[++t]=to[i];
}
}
}
int main()
{
scanf("%d%d%d%d",&n,&m,&nx,&ny); deg1[ny]++;
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
deg[y]++;
deg1[y]++;
}
// int ans=1;
for(int i=2;i<=n;i++)
ans=(1ll*ans*deg1[i])%mod;
if(ny==1)
{
printf("%d\n",ans);
return 0;
}
// cout<<ans<<endl;
getinv();
// cout<<ans<<endl;
topologicalsort();
printf("%d\n",(ans-dp[nx]+mod)%mod);
}