bzoj4011:[HNOI2015]落忆枫音

传送门

首先考虑不加边,那么就是一个有向无环图
答案的统计就是\(\sum_{i=2}^{n}in[i]\)\(in[i]\)就是\(i\)号点的入度)
但是考虑加边之后会出现环,那么就会导致重复计数,需要将重复的部分去掉
重复的原因就是因为环边可能会走到重复的点
现在考虑新加的边\(x->y\)
我们可以发现,只有\(y->x\)的路径会被重复计算,计算出包含这条路径的树有多少种方案,减掉就好了
因为在DAG上,拓扑序dp
代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
void read(int &x) {
	char ch; bool ok;
	for(ok=0,ch=getchar(); !isdigit(ch); ch=getchar()) if(ch=='-') ok=1;
	for(x=0; isdigit(ch); x=x*10+ch-'0',ch=getchar()); if(ok) x=-x;
}
#define rg register
const int maxn=1e5+10,mod=1e9+7;bool vis[maxn];int f[maxn];queue<int>q;
int cnt,n,m,x,y,ans=1,in[maxn],pre[maxn*2],nxt[maxn*2],h[maxn],ny[maxn];
void add(int x,int y){pre[++cnt]=y,nxt[cnt]=h[x],h[x]=cnt;}
int mi(int a,int b)
{
	int ans=1;
	while(b)
	{
		if(b&1)ans=1ll*ans*a%mod;
		b>>=1,a=1ll*a*a%mod;
	}
	return ans;
}
void dfs()
{
	queue<int>q;
	f[y]=1ll*ans*ny[y]%mod;
	for(rg int i=1;i<=n;i++)if(!in[i])q.push(i);
	while(!q.empty())
	{
		int x=q.front();q.pop();
		for(rg int i=h[x];i;i=nxt[i])
		{
			(f[pre[i]]+=(1ll*f[x]*ny[pre[i]])%mod)%=mod;
			if(!(--in[pre[i]]))q.push(pre[i]);
		}
	}
	ans=(ans-f[x]+mod)%mod;
}
int main()
{
	read(n),read(m),read(x),read(y),in[y]++;
	for(rg int i=1,x,y;i<=m;i++)read(x),read(y),in[y]++,add(x,y);
	for(rg int i=2;i<=n;i++)ans=1ll*ans*in[i]%mod;
	if(y==1){printf("%d\n",ans);return 0;}
	for(rg int i=1;i<=n;i++)ny[i]=mi(in[i],mod-2);in[y]--;
	dfs();printf("%d\n",ans);
}
posted @ 2019-03-20 19:39  蒟蒻--lichenxi  阅读(122)  评论(0编辑  收藏  举报