您的位置:首页 > 其它

bzoj 4011: [HNOI2015]落忆枫音 递推

2016-05-02 22:11 471 查看
计数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
,pnt[N<<1],nxt[N<<1],dgr
,num
,inv
,h
,f
;
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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: