[BZOJ4011][HNOI2015]落忆枫音

bzoj
luogu

sol

首先考虑不加那条边怎么做吧。

只要给每个节点选定一个父亲(1号点除外),那么这棵树就可以被唯一确定。

很显然吧。
由于这是一个(DAG),所以你不管怎么选父亲节点,都不会出现环。
所以在(DAG)上的答案就是(prod_{i=1}^{n}du_i)。(你就当(du_1=1)好了)
现在考虑在(DAG)上加了一条边,那么就可能产生若干个环(注意是若干个而不是一个)。
这时候按照上述的随便选定父亲节点的问题在于,可能选出了一个环。
那样就要求这个环上的每一条边都要被选。
然后不在这个环上的点的父亲随便怎么选这种方案都是不合法的。
所以对于一个环(a_1,a_2...a_k),需要减去的部分就应该是(frac{prod_{i=1}^{n}du_i}{prod_{i=1}^{k}du_{a_i}})
这个东西在(DAG)上跑一遍(dp)就可以了。
时间复杂度(O(n+m))

code

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int gi()
{
	int x=0,w=1;char ch=getchar();
	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
	if (ch=='-') w=0,ch=getchar();
	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return w?x:-x;
}
const int N = 2e5+5;
const int mod = 1e9+7;
int n,m,xx,yy,to[N],nxt[N],head[N],cnt,du[N],f[N],ans=1,sum=1;
void link(int u,int v){to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt;++du[v];}
int fastpow(int a,int b)
{
	int res=1;
	while (b) {if (b&1) res=1ll*res*a%mod;a=1ll*a*a%mod;b>>=1;}
	return res;
}
int dfs(int u)
{
	if (~f[u]) return f[u];
	if (u==xx) return f[u]=1ll*sum*fastpow(du[u],mod-2)%mod;
	f[u]=0;
	for (int e=head[u];e;e=nxt[e])
		(f[u]+=dfs(to[e]))%=mod;
	return f[u]=1ll*f[u]*fastpow(du[u],mod-2)%mod;
}
int main()
{
	n=gi();m=gi();xx=gi();yy=gi();
	for (int i=1,u,v;i<=m;++i)
		u=gi(),v=gi(),link(u,v);
	++du[1];
	for (int i=1;i<=n;++i)
	{
		if (i==yy) ans=1ll*ans*(du[i]+1)%mod;
		else ans=1ll*ans*du[i]%mod;
		sum=1ll*sum*du[i]%mod;
	}
	memset(f,-1,sizeof(f));
	ans=(ans-dfs(yy)+mod)%mod;
	printf("%d
",ans);return 0;
}
原文地址:https://www.cnblogs.com/zhoushuyu/p/8690831.html