【HNOI2015】落忆枫音

题面

题解

求一个有特殊性质的有向图的生成树的个数。

首先,有向图的生成树的个数可以用矩阵树定理,能够得到\(40\)分。

但是如果它是一个\(\mathrm{DAG}\)就很好做,枚举每一个点的父亲,答案就是\(\prod d[i]\)\(d\)是每个点的入度

发现加了一条边之后只会形成一个环,设环上的点为\(a_1, a_2, \cdots, a_k\),那么形成的不合法的生成树有\(\frac{\prod_i d[i]}{\prod_{i = 1} ^ k d[a_i]}\)种。

于是答案就是\(\prod_i d[i] - \frac{\prod_i d[i]}{\prod_{i = 1} ^ k d[a_i]}\)

代码

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#define RG register
#define clear(x, y) memset(x, y, sizeof(x))

inline int read()
{
	int data = 0, w = 1; char ch = getchar();
	while(ch != '-' && (!isdigit(ch))) ch = getchar();
	if(ch == '-') w = -1, ch = getchar();
	while(isdigit(ch)) data = data * 10 + (ch ^ 48), ch = getchar();
	return data * w;
}

const int maxn(100010), maxm(200010), Mod(1000000007);
struct edge { int next, to; } e[maxm];
int head[maxn], e_num, n, m, vis[maxn], deg[maxn];
int sx, sy, ans = 1, dmul = 1, f[maxn];
inline void add_edge(int from, int to)
{
	e[++e_num] = (edge) {head[from], to};
	head[from] = e_num;
}

int fastpow(int x, int y)
{
	int ans = 1;
	for(; y; y >>= 1, x = 1ll * x * x % Mod)
		if(y & 1) ans = 1ll * ans * x % Mod;
	return ans;
}

void dfs(int x)
{
	if(vis[x]) return; vis[x] = 1;
	if(x == sy) return (void) (f[x] = 1ll * dmul
			* fastpow(deg[x], Mod - 2) % Mod);
	for(RG int i = head[x]; i; i = e[i].next)
		dfs(e[i].to), f[x] = (f[x] + f[e[i].to]) % Mod;
	f[x] = 1ll * f[x] * fastpow(deg[x], Mod - 2) % Mod;
}

int main()
{
	n = read(), m = read(), sx = read(), sy = read();
	for(RG int i = 1, a, b; i <= m; i++)
		a = read(), b = read(), add_edge(b, a), ++deg[b];
	++deg[1];
	for(RG int i = 1; i <= n; i++)
	{
		if(i == sy) ans = 1ll * ans * (deg[i] + 1) % Mod;
		else ans = 1ll * ans * deg[i] % Mod;
		dmul = 1ll * dmul * deg[i] % Mod;
	}
	dfs(sx); ans = (ans - f[sx] + Mod) % Mod;
	printf("%d\n", ans);
	return 0;
}
posted @ 2019-02-21 14:37  xgzc  阅读(231)  评论(0编辑  收藏  举报