BZOJ 4011 落忆枫音

Description

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

Input

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

Output

输出一行,为添加了从穴位(x)连向穴位(y)的脉络后,枫叶上以穴位(1)为根的脉络树的方案数对(10^{9}+7)取模得到的结果。

Sample Input

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

Sample Output

3

Hint

对于所有测试数据,(1 le n le 100000)(n - 1 le m le min(200000, n(n–1)/2),1 le x,y,u_{i}, v_{i} le n)

如果图中无环,那么$$ans = prod_{i=2}^{n}indegree_{i}$$

有环的话我们就将不合法的贡献减去即可。出现了环,必定选择了(x o y)的这条边,然后再选择了(y o x)的某条路径。

暴力做法是枚举所有(y o x)的所有路径,去掉该路径(path_{k})上的所有点,该路径对答案的非法贡献即为$$prod_{i=2}^{n}lbrack i otin path_{k} brack indegree_{i}$$
设$$U=prod_{i=2}^{n}prod_{i}$$
那么路径(path_{k})的非法贡献就变为$$Uprod_{i=2}^{n}lbrack i in path_{k} brack indegree_{i}^{-1}$$
有了这个之后我们就可以优化暴力,变为dp,求出(y o x)的所有路径上点的逆元积的和。(f_{i})表示从(y o i)路径上所有路径上点的逆元积的和,topsort转移脑补即可。最后$$ans=U-U imes f_{x}$$很好打,考试时居然没有往这方面想。

#include<queue>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;

typedef long long ll;
#define maxn (100010)
#define rhl (1000000007)
int n,m,X,Y,side[maxn],toit[maxn*2],next[maxn*2],d[maxn];
int tot,cnt,ind[maxn]; ll ans = 1,f[maxn]; bool in[maxn];

inline void add(int a,int b) { next[++cnt] = side[a]; side[a] = cnt; toit[cnt] = b; ind[b]++; d[b]++; }

inline ll qsm(ll a,int b)
{
	ll ret = 1;
	for (;b;b >>= 1,(a *= a)%=rhl) if (b & 1) (ret *= a) %= rhl;
	return ret;
}

inline void topsort()
{
	queue <int> team; 
	for (int i = 1;i <= n;++i) if (!ind[i]) team.push(i);
	while (!team.empty())
	{
		int now = team.front(); team.pop();
		if (now == Y) f[now] = 1;
		if (now == 1) f[now] = 0;
		else (f[now] *= qsm(d[now],rhl-2)) %= rhl;
		for (int i = side[now];i;i = next[i])
		{
			f[toit[i]] += f[now]; if (f[toit[i]] >= rhl) f[toit[i]] -= rhl;
			if (!--ind[toit[i]]) team.push(toit[i]);
		}
	}
}

int main()
{
	freopen("4011.in","r",stdin);
	freopen("4011.out","w",stdout);
	scanf("%d %d %d %d",&n,&m,&X,&Y);
	for (int i = 1,u,v;i <= m;++i) scanf("%d %d",&u,&v),add(u,v);
	++d[Y];
	for (int i = 2;i <= n;++i) (ans *= (ll)d[i]) %= rhl;	
	topsort();
	(ans -= ans*f[X])%=rhl; if (ans < 0) ans += rhl;
	printf("%lld",ans);
	fclose(stdin); fclose(stdout);
	return 0;
}
原文地址:https://www.cnblogs.com/mmlz/p/4448742.html