[bzoj4011] [HNOI2015]落忆枫音

题目描述

不妨假设枫叶上有 n个穴位,穴位的编号为 1 ~ n。有若干条有向的脉络连接着这些穴位。穴位和脉络组成一个有向无环图——称之为脉络图(例如图 1),穴位的编号使得穴位 1 没有从其他穴位连向它的脉络,即穴位 1 只有连出去的脉络;由上面的故事可知,这个有向无环图存在一个树形子图,它是以穴位 1为根的包含全部n个穴位的一棵树——称之为脉络树(例如图 2和图 3给出的树都是图1给出的脉络图的子图);值得注意的是,脉络图中的脉络树方案可能有多种可能性,例如图2和图 3就是图 1给出的脉络图的两个脉络树方案。

img

脉络树的形式化定义为:以穴位 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 取模得到的结果。

Input

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

Output

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

Sample Input

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

Sample Output

3

Solution

对于一个\(DAG\),答案显然是:

\[\prod_{i=2}^{n}deg_i \]

其中\(deg_i\)表示\(i\)的入度,每个点选一个点做父亲,一定可以构成一棵树。

加入了一条边,就多出了一些环,先不管这些环,还是按上面把答案算出来,

显然,多算了一些环的情况,可以发现,构造出来的图最多出现一个环。

所以暴力的话就直接枚举每一个环,答案就是:

\[\prod_{i=2}^ndeg_i-\sum\prod_{i\notin circle} deg_i \]

后面的连乘也就是:

\[\cfrac{\prod_{i=2}^{n}deg_i}{\prod_{i\in circle}deg_i} \]

考虑到每个环一定是由\((x,y)\)\(y \to x\)的一条路径构成的,所以设\(g(u)\)\(u\)\(x\)的路径上上面式子的贡献,转移就是:

\[g(u)=\cfrac{\sum_{(u,v)\in E}g(v)}{deg_u} \]

其中\(E\)为边集。

然后答案就是:

\[\prod_{i=2}^ndeg_i-g(y) \]

#include<bits/stdc++.h>
using namespace std;
 
void read(int &x) {
    x=0;int f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
    for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}
 
void print(int x) {
    if(x<0) putchar('-'),x=-x;
    if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}

const int maxn = 2e5+10;
const int mod = 1e9+7;

int inv[maxn],n,m,g[maxn],head[maxn],tot,x,y,deg[maxn],ans=1,vis[maxn];
struct edge{int to,nxt;}e[maxn<<1];

void ins(int u,int v) {e[++tot]=(edge){v,head[u]},head[u]=tot;}

void dfs(int u) {
	if(vis[u]) return ;vis[u]=1;
	if(u==x) return g[u]=1ll*ans*inv[deg[u]]%mod,void();
	for(int i=head[u];i;i=e[i].nxt)
		dfs(e[i].to),g[u]=(g[u]+g[e[i].to])%mod;
	g[u]=1ll*g[u]*inv[deg[u]]%mod;
}

int main() {
	inv[1]=1;
	for(int i=2;i<maxn;i++) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
	read(n),read(m),read(x),read(y);deg[1]=1;
	for(int i=1,u,v;i<=m;i++) read(u),read(v),ins(u,v),deg[v]++;
	for(int i=2;i<=n;i++) ans=1ll*ans*deg[i]%mod;
	dfs(y);int delta=g[y];
	ans=1ll*ans*inv[deg[y]]%mod*(deg[y]+1)%mod;
	write((ans-delta+mod)%mod);
	return 0;
}
posted @ 2018-12-26 16:20  Hyscere  阅读(94)  评论(0编辑  收藏  举报