[HNOI2015]落忆枫音 题解

文章讨论了一个涉及有向无环图(DAG)的问题,其中目标是计算在图中添加一条边后,以特定节点为根的脉络树的不同方案数。提出了使用入度乘积的方法来表示生成树的方案数,并通过动态规划计算环内外的贡献,以排除形成环的非法方案。给出了转移方程和代码实现。
摘要由CSDN通过智能技术生成

题目背景

题目描述

不妨假设枫叶上有 n个穴位,穴位的编号为 1 ~ n。有若干条有向的脉络连接着这些穴位。穴位和脉络组成一个有向无环图——称之为脉络图(例如图 1),穴位的编号使得穴位 1 没有从其他穴位连向它的脉络,即穴位 1 只有连出去的脉络;由上面的故事可知,这个有向无环图存在一个树形子图,它是以穴位 1为根的包含全部n个穴位的一棵树——称之为脉络树(例如图 2和图 3给出的树都是图1给出的脉络图的子图);值得注意的是,脉络图中的脉络树方案可能有多种可能性,例如图2和图 3就是图 1给出的脉络图的两个脉络树方案。 脉络树的形式化定义为:以穴位 r 为根的脉络树由枫叶上全部 n个穴位以及 n- 1 条脉络组成,脉络树里没有环,亦不存在从一个穴位连向自身的脉络,且对于枫叶上的每个穴位 s,都存在一条唯一的包含于脉络树内的脉络路径,使得从穴位r 出发沿着这条路径可以到达穴位 s。 现在向脉络图添加一条与已有脉络不同的脉络(注意:连接 2个穴位但方向不同的脉络是不同的脉络,例如从穴位3到4的脉络与从4到3的脉络是不同的脉络,因此,图 1 中不能添加从 3 到 4 的脉络,但可添加从 4 到 3 的脉络),这条新脉络可以是从一个穴位连向自身的(例如,图 1 中可添加从 4 到 4 的脉络)。原脉络图添加这条新脉络后得到的新脉络图可能会出现脉络构成的环。 请你求出添加了这一条脉络之后的新脉络图的以穴位 1 为根的脉络树方案数。 由于方案可能有太多太多,请输出方案数对 1,000,000,007 取模得到的结果。

输入输出格式

输入格式

输入文件的第一行包含四个整数 n、m、x和y,依次代表枫叶上的穴位数、脉络数,以及要添加的脉络是从穴位 x连向穴位y的。 接下来 m行,每行两个整数,由空格隔开,代表一条脉络。第 i 行的两个整数为ui和vi,代表第 i 条脉络是从穴位 ui连向穴位vi的。

输出格式

输出一行,为添加了从穴位 x连向穴位 y的脉络后,枫叶上以穴位 1 为根的脉络树的方案数对 1,000,000,007取模得到的结果。

输入输出样例

输入样例 #1

4 4 4 3
1 2
1 3
2 4
3 2

输出样例 #1

3

说明

对于所有测试数据, 1 ≤ n ≤ 100000 , n − 1 ≤ m ≤ m i n ( 200000 , n ( n − 1 ) 2 ) , 1 ≤ x , y , u i , v i ≤ n 1 \leq n \leq 100000, n - 1 \leq m \leq min(200000, \frac {n(n -1)} {2}),1 \leq x, y, u_i, v_i \leq n 1n100000,n1mmin(200000,2n(n1)),1x,y,ui,vin

SOLUTION

首先我们知道:

若是对于DAG(有向无环图),生成树的方案数为 ∏ i ∈ S d ( i ) \prod_{i\in S} d(i) iSd(i)

d ( i ) d(i) d(i) i i i的入度

(证明略,可以自行1秒思考)

因为现在多加了一条边,那么就有一个环

所以我们考虑如何算出不合法的部分

设加了 ( x , y ) (x,y) (x,y)这条边

当换上的边都被选中时就是不合法的

因此

s u m = ∏ i ∈ S d ( i ) sum=\prod_{i\in S} d(i) sum=iSd(i)

那么不合法的方案就是: s u m ∏ i ∈ T d ( i ) \frac{sum}{\prod_{i\in T}d(i)} iTd(i)sum

其中T为 x → y x\to y xy的环上

算的其实就是将环外的点连成树,再选了这个环的方案数

(由于这是要选树型图,肯定只选n-1条边,也就是每个点(除了点1)都只有一个入边,所以若选了这个环,那么环之内的图的入边就已经满了,所以不合法的情况只可能是环外的点单独形成一个树形图+选了环)

对于统计 s u m ∏ i ∈ T d ( i ) \frac{sum}{\prod_{i\in T}d(i)} iTd(i)sum

我们可以设 g ( i ) g(i) g(i)为环上 i i i点到 y y y点的 d d d的:

g ( i ) = s u m ∏ u ∈ ( i → y ) d ( u ) g(i)=\frac{sum}{\prod_{u\in (i\to y)}d(u)} g(i)=u(iy)d(u)sum

所以边界: g ( y ) = s u m d ( y ) g(y)=\frac{sum}{d(y)} g(y)=d(y)sum

转移方程: g ( i ) = ∑ g ( t o ) d ( i ) g(i)=\frac{\sum{g(to)}}{d(i)} g(i)=d(i)g(to)

(关于转移方程的疑问)

y → x y\to x yx不只一条路径(应该是这样的)

所以上面的可能在表述上有点问题(但是问题不大)

统计的是每一个环内*环外的方案之和

假设当前点为 i i i,有两个点 x 1 , x 2 x1,x2 x1,x2都可以到达 x x x

并且 x 1 , x 2 x1,x2 x1,x2都直接连着 i i i

那么答案=两个环的 s u m ∏ u ∈ ( i → y ) d ( u ) \frac{sum}{\prod_{u\in (i\to y)}d(u)} u(iy)d(u)sum之和

也就= ( s u m ∏ u ∈ x → x 1 d ( u ) + s u m ∏ u ∈ x → x 2 d ( u ) ) ∏ u ∈ i → y \frac{(\frac{sum}{\prod_{u\in{x\to x1}} d(u)}+\frac{sum}{\prod_{u\in{x\to x2}} d(u)})}{\prod_{u\in{i\to y}}} uiy(uxx1d(u)sum+uxx2d(u)sum)

所以转移方程: g ( i ) = ∑ g ( t o ) d ( i ) g(i)=\frac{\sum{g(to)}}{d(i)} g(i)=d(i)g(to)

over!(讲的应该还是比较清晰的)

CODE

#include<bits/stdc++.h>
// 给定一张有向无环图,现在要求加入一条边,求加入后以1为根的树形图个数
#define ll long long
using namespace std;
const ll N=2e6+2,P=1e9+7;
ll n,m,X,Y,x,y,hd[N],vis[N],to[N],nxt[N],g[N];
ll dsum=1,ans=1,d[N],cnt;
void add(ll x,ll y){
	to[++cnt]=y;
	nxt[cnt]=hd[x];
	hd[x]=cnt;
}
ll qpow(ll a,ll b){
	ll res=1;
	while(b){
		if(b&1)res=res*a%P;
		a=a*a%P;
		b>>=1;
	}
	return res;
}
void dfs(ll x){
	if(vis[x])return ;
	vis[x]=1;
	if(x==Y){
		g[x]=dsum*qpow(d[x],P-2)%P;
		return ;
	}
	for(ll i=hd[x];i;i=nxt[i]){
		ll y=to[i];
		dfs(y);
		(g[x]+=g[y])%=P;
	}
	(g[x]*=qpow(d[x],P-2))%=P;
}
int main(){
	scanf("%lld%lld%lld%lld",&n,&m,&X,&Y);
	for(ll i=1;i<=m;i++){
		scanf("%lld%lld",&x,&y);
		add(y,x);d[y]++;
	} 
	d[1]++;
	for(ll i=1;i<=n;i++){
		if(i==Y)(ans*=(d[i]+1))%=P;
		else (ans*=d[i])%=P;
		(dsum*=d[i])%=P;
	}
	dfs(X);
	ans=(ans+P-g[X])%P;
	printf("%lld",ans);
	return 0;
}

w完结撒花❀

★,°:.☆( ̄▽ ̄)/$:.°★

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值