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  阅读(14)  评论(0编辑  收藏  举报
var canShowAdsense=function(){return !!0};