BZOJ3456 城市规划

题面

洛谷

\(\mathrm{bzoj}\)权限题

一句话题面

\(n\)个点的无向连通图个数。

\(n\leq 130000\)

题解

首先我们知道,\(n\)个点的无向图个数为\(\mathrm{g}(n) = 2^{\mathrm{C}_n^2}\)

\(n\)个点的无向连通图的个数为\(\mathrm{f}(n)\)

那么我们枚举\(1\)号点所在的连通块的大小\(i\),可以知道

\[\mathrm{g}(n) = \sum_{i=1}^n \binom{n - 1}{i - 1} \mathrm{f}(i)\mathrm{g}(n-i) \]

拆开组合数:

\[\begin{aligned} \mathrm{g}(n) &= \sum_{i=1}^n \frac{(n-1)!}{(i-1)!(n-i)!}\mathrm{f}(i)\mathrm{g}(n-i) \\ \frac{\mathrm{g}(n)}{(n-1)!} &= \sum_{i=1}^n \frac{\mathrm{f}(i)}{(i-1)!} \frac{\mathrm{g}(n-i)}{(n-i)!} \end{aligned} \]

构造生成函数:

\[\begin{aligned} \mathcal{F}(x) &= \sum_{i=1}^\infty \frac{\mathrm{f}(i)}{(i-1)!}x^i \\ \mathrm{G_1}(x) &= \sum_{i=0}^\infty \frac{\mathrm{g}(i)}{i!}x^i \\ \mathrm{G_2}(x) &= \sum_{i=1}^\infty \frac{\mathrm{g}(i)}{(i-1)!}x^i \end{aligned} \]

那么\(\mathrm{G_2} = \mathcal{F}*\mathrm{G_1}, \mathcal{F} = \mathrm{G_1} * \mathrm{G_2^{-1}}\)

多项式求逆即可

代码

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#define RG register
#define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#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(400010), Mod(1004535809);
int n, fac[maxn], inv[maxn], Pow[maxn];

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

int r[maxn];
template<int opt> void FFT(int *p, int N)
{
	for(RG int i = 0; i < N; i++) if(i < r[i]) std::swap(p[i], p[r[i]]);
	for(RG int i = 1; i < N; i <<= 1)
	{
		int rot = fastpow(3, (Mod - 1) / (i << 1));
		for(RG int j = 0; j < N; j += (i << 1))
			for(RG int k = 0, w = 1; k < i; ++k, w = 1ll * w * rot % Mod)
			{
				int x = p[j + k], y = 1ll * w * p[i + j + k] % Mod;
				p[j + k] = (x + y) % Mod, p[i + j + k] = (x - y + Mod) % Mod;
			}
	}
	if(opt == -1) std::reverse(p + 1, p + N);
}

void Inv(int *a, int *b, int now)
{
	static int c[maxn];
	if(now == 1) return (void) (*b = fastpow(*a, Mod - 2));
	Inv(a, b, (now + 1) >> 1); int P = -1, len = 1;
	while(len < (now << 1)) len <<= 1, ++P;
	for(RG int i = 0; i < len; i++) r[i] = (r[i >> 1] >> 1) | ((i & 1) << P);
	std::copy(a, a + now, c), std::fill(c + now, c + len, 0);
	FFT<1>(c, len), FFT<1>(b, len);
	for(RG int i = 0; i < len; i++)
		b[i] = 1ll * (2 - 1ll * b[i] * c[i] % Mod + Mod) % Mod * b[i] % Mod;
	FFT<-1>(b, len); int invn = fastpow(len, Mod - 2);
	for(RG int i = 0; i < len; i++) b[i] = 1ll * b[i] * invn % Mod;
	std::fill(b + now, b + len, 0);
}

int F[maxn], G1[maxn], G2[maxn], A[maxn];
int main()
{
#ifndef ONLINE_JUDGE
	file(cpp);
#endif
	n = read(), inv[0] = fac[0] = Pow[0] = Pow[1] = 1;
	for(RG int i = 1; i <= n; i++) fac[i] = 1ll * fac[i - 1] * i % Mod;
	inv[n] = fastpow(fac[n], Mod - 2);
	for(RG int i = n - 1; i; i--) inv[i] = 1ll * inv[i + 1] * (i + 1) % Mod;
	for(RG int i = 2; i <= n; i++)
		Pow[i] = fastpow(2, 1ll * i * (i - 1) / 2 % (Mod - 1));
	for(RG int i = 0; i <= n; i++) G1[i] = 1ll * Pow[i] * inv[i] % Mod;
	for(RG int i = 1; i <= n; i++) G2[i] = 1ll * Pow[i] * inv[i - 1] % Mod;
	Inv(G1, A, n + 1); int N = 1, P = -1;
	while(N <= (n << 1)) N <<= 1, ++P;
	for(RG int i = 0; i < N; i++) r[i] = (r[i >> 1] >> 1) | ((i & 1) << P);
	FFT<1>(A, N), FFT<1>(G2, N);
	for(RG int i = 0; i < N; i++) F[i] = 1ll * A[i] * G2[i] % Mod;
	FFT<-1>(F, N); int invn = fastpow(N, Mod - 2);
	F[n] = 1ll * F[n] * invn % Mod;
	printf("%lld\n", 1ll * F[n] * fac[n - 1] % Mod);
	return 0;
}
posted @ 2019-01-30 10:53  xgzc  阅读(153)  评论(0编辑  收藏  举报