bzoj4011 落忆枫音 递推

       计数dp(递推)裸题(跟zj的题真心不能比啊)。。。

       假设没有那个加边。那么显然答案就是所有点的入度相乘的结果(不包括1),显然每个点都可以随意选择一个入边,那么由于原图是DAG,因此选完n-1条边之后一定连通且无环,因此必然合法。

       一开始斯波看错题以为是随意加一条边然后统计总方案数

       那么加入S->T这条边之后,不妨仍然按照上述方法直接求答案;然后考虑产生环的不合法的方案。显然环中必有S->T这条边,以及T->S的一条路径。如果枚举这条路径,那么一条路径的贡献就是除了这条路径以外的点的入度的积。令f[x]表示T->x的答案,那么根据定义有f[y]=Σf[x]/dgree[y],(x,y)∈E。按topo序dp即可。

AC代码如下:

#include<iostream>
#include<cstdio>
#define mod 1000000007
#define ll long long
#define N 100005
using namespace std;

int n,m,sta,gol,tot,fst[N],pnt[N<<1],nxt[N<<1],dgr[N],num[N],inv[N],h[N],f[N];
int read(){
	int x=0; char ch=getchar();
	while (ch<'0' || ch>'9') ch=getchar();
	while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }
	return x;
}
void add(int x,int y){
	pnt[++tot]=y; nxt[tot]=fst[x]; fst[x]=tot;
}
int main(){
	n=read(); m=read(); sta=read(); gol=read(); int i,x,y;
	inv[0]=inv[1]=1;
	for (i=2; i<=n; i++) inv[i]=mod-(ll)inv[mod%i]*(mod/i)%mod;
	for (i=1; i<=m; i++){
		x=read(); y=read(); add(x,y);
		dgr[y]++;
	}
	for (i=1; i<=n; i++) num[i]=dgr[i];
	dgr[gol]++; int ans=1;
	for (i=2; i<=n; i++) ans=(ll)ans*dgr[i]%mod;
	if (gol==1){ printf("%d\n",ans); return 0; }
	f[gol]=ans;
	int head=0,tail=0;
	for (i=1; i<=n; i++) if (!dgr[i]) h[++tail]=i;
	while (head<tail){
		x=h[++head]; f[x]=(ll)f[x]*inv[dgr[x]]%mod;
		if (x==sta) break;
		for (i=fst[x]; i; i=nxt[i]){
			y=pnt[i]; num[y]--;
			f[y]+=f[x]; if (f[y]>=mod) f[y]-=mod;
			if (!num[y]) h[++tail]=y;
		}
	}
	printf("%d\n",(ans-f[sta]+mod)%mod);
	return 0;
}

by lych

2016.5.2

你可能感兴趣的:(动态规划,拓扑排序,递推,计数dp)