LGP3244题解

考虑正常 DAG 的有向生成树的方案数。

很明显发现,每个节点只需要挑一个父亲即可。方案数为 \(\prod_{i=2}^nd[i]\)。

再考虑加上新边后新增的 DAG 数量。

将点分为两类。假设这条新边是 \((s,t)\),那么我们将能到达 \(s\) 且能够被 \(t\) 到达的节点拎出来算一类,剩下的节点算另一类。

因为是考虑父亲,所以二类节点的贡献仍然是度数。考虑一类节点的贡献。

一类节点的贡献相当于 \(t\) 无法到达 \(s\) 的树的方案数。正难则反,考虑有多少种方法使得 \(t\) 能够到达 \(s\)

总方案数是很容易计算的,度数之积。

我们每次钦定一条从 \(t\) 到 \(s\) 的链,然后强制钦定这些边都要连上。

其他节点的贡献仍然是度数之积。

我们让度数之积最后乘上,变成钦定节点的度数之积的逆元。

我们现在做的问题相当于从 \(t\) 到达 \(s\) 的每条路径的权值之积,随便搞个 DP 统计一下就好了。

复杂度是线性的。(逆元可以预处理)

#include<cstdio>
typedef unsigned ui;
const ui M=1e5+5,mod=1e9+7;
ui n,m,s,t,cnt[2],h[2][M],d[M],deg[M],inv[M],dp[M];bool v1[M],v2[M];ui L,R,q[M];
struct Edge{
	ui v,nx;
}e[2][M<<1];
inline void Add(const ui&id,const ui&u,const ui&v){
	e[id][++cnt[id]]=(Edge){v,h[id][u]};h[id][u]=cnt[id];
}
signed main(){
	ui ans(1);
	scanf("%u%u%u%u",&n,&m,&s,&t);inv[1]=1;
	for(ui i=2;i<=n;++i)inv[i]=1ull*(mod-mod/i)*inv[mod%i]%mod;
	while(m--){
		ui u,v;scanf("%u%u",&u,&v);
		Add(0,u,v);Add(1,v,u);++d[v];
	}
	for(ui i=2;i<=n;++i)ans=1ull*ans*d[i]%mod;
	for(ui i=1;i<=n;++i)deg[i]=d[i];
	q[L=R=1]=t;v1[t]=true;
	while(L<=R){
		ui u=q[L++];
		for(ui v,E=h[0][u];E;E=e[0][E].nx)if(!v1[v=e[0][E].v]){
			v1[v]=true;q[++R]=v;
		}
	}
	q[L=R=1]=s;v2[s]=true;
	while(L<=R){
		ui u=q[L++];
		for(ui v,E=h[1][u];E;E=e[1][E].nx)if(!v2[v=e[1][E].v]){
			v2[v]=true;q[++R]=v;
		}
	}
	for(ui i=1;i<=n;++i)if(!v1[i]||!v2[i]){
		for(ui E=h[0][i];E;E=e[0][E].nx)--deg[e[0][E].v];
	}
	dp[t]=1;q[L=R=1]=t;
	while(L<=R){
		ui u=q[L++];
		if(u!=t)dp[u]=1ull*dp[u]*inv[d[u]]%mod;
		for(ui v,E=h[0][u];E;E=e[0][E].nx)if(v=e[0][E].v,v1[v]&&v2[v]){
			dp[v]=(dp[v]+dp[u])%mod;
			if(!--deg[v])q[++R]=v;
		}
	}
	printf("%u",1ull*ans*(1+1ull*inv[d[t]]*(1+mod-dp[s])%mod)%mod);
}
posted @ 2022-03-02 15:18  Prean  阅读(59)  评论(0)    收藏  举报
var canShowAdsense=function(){return !!0};