拓扑序dp - 落忆枫音(BZOJ 4011 HNOI2015)

传送门


Analysis

莫名其妙地被题面虐了一把
很好的一道结论题(?)

朱刘算法的推论可知,如果除根节点外每个点都选择一条入边,由于没有环,因此一定会形成一个树形图
答案就是 ∏ i = 2 n d e g r e e [ i ] ∏^n_{i=2}degree[i] i=2ndegree[i] 其中degree[i]表示第i个点的入度
但是现在我们加入了一条边,图中就可能形成环
我们需要做的就是,依然用上述式子算答案,再减去不合法的环的情况
不合法 环的情况,就是
∑ S 是 原 图 中 y → x 的 一 条 路 径 的 点 集 ∏ 2 ≤ j ≤ n , j ∉ S d e g r e e j \sum_{S是原图中y\rightarrow x的一条路径的点集}\prod_{2\leq j\leq n,j\notin S}degree_j Syx2jn,j/Sdegreej

翻译为人话,就是说:现在强制选择环,然后其他的点随便选的不合法方案数
那么我们的目标就是求出上面那个式子,怎么求呢?
可以考虑dp,状态转移

f i = ∑ j → i f j d e g r e e i f_i=\frac{\sum_{j\rightarrow i}f_j}{degree_i} fi=degreeijifj j j j是可以到达 i i i的点)


Code
#include<bits/stdc++.h>
#define in read()
#define N 100009
#define M 200010
#define P 1000000007
using namespace std;
inline int read(){
	char ch;int f=1,res=0;
	while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
	while(ch>='0'&&ch<='9'){res=(res<<3)+(res<<1)+ch-'0';ch=getchar();}
	return f==1?res:-res;
}
int n,m,x,y,ans=1;
int f[N],inv[M];
int deg[N],__deg[N];
int nxt[M],to[M],head[N],ecnt=0;
inline void add(int x,int y){nxt[++ecnt]=head[x];head[x]=ecnt;to[ecnt]=y;}
inline int mul(int a,int b){return a*1ll*b%P;}
inline void topsort(){
	queue<int> q;
	for(int i=1;i<=n;++i)
		if(!deg[i]) q.push(i);
	while(!q.empty()){
		int u=q.front();q.pop();
		f[u]=(f[u]*1ll*inv[__deg[u]])%P;
		for(int e=head[u];e;e=nxt[e]){
			int v=to[e];
			f[v]=(f[u]+f[v])%P;
			--deg[v];
			if(!deg[v]) q.push(v);
		}
	}
}
inline void init(){
	inv[1]=1;
	for(int i=2;i<=m+1;++i)
		inv[i]=(P-P/i)*1ll*inv[P%i]%P; 
}
int main(){
	n=in;m=in;x=in;y=in;
	__deg[y]++;
	init();
	int i,j,k,u,v;
	for(i=1;i<=m;++i){
		u=in;v=in;
		add(u,v);deg[v]++;__deg[v]++;
	}
	for(i=2;i<=n;++i) ans=mul(ans,__deg[i]);
	if(y==1){
		printf("%d",ans);
		return 0;
	}
	f[y]=ans;
	topsort();
	printf("%d",(ans-f[x]+P)%P);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值