[BZOJ3456]城市规划

[BZOJ3456]城市规划

试题描述

刚刚解决完电力网络的问题, 阿狸又被领导的任务给难住了.

刚才说过, 阿狸的国家有 \(n\) 个城市, 现在国家需要在某些城市对之间建立一些贸易路线, 使得整个国家的任意两个城市都直接或间接的连通. 为了省钱, 每两个城市之间最多只能有一条直接的贸易路径. 对于两个建立路线的方案, 如果存在一个城市对, 在两个方案中是否建立路线不一样, 那么这两个方案就是不同的, 否则就是相同的. 现在你需要求出一共有多少不同的方案.

好了, 这就是困扰阿狸的问题. 换句话说, 你需要求出 \(n\) 个点的简单(无重边无自环)无向连通图数目.

由于这个数字可能非常大, 你只需要输出方案数 \(\mod 1004535809(479 \times 2 ^ {21} + 1)\) 即可.

输入

仅一行一个整数 \(n(\le 130000)\)

输出

仅一行一个整数, 为方案数 \(\mod 1004535809\).

输入示例

3

输出示例

4

数据规模及约定

对于 \(100\%\) 的数据, \(n \le 130000\)

题解

\(f(n)\) 表示 \(n\) 个点的带标号简单无向连通图的个数,\(g(n)\) 表示 \(n\) 个点带标号简单无向图的个数,则显然有 \(g(n) = 2^{C_n^2}\)

\(f(n)\) 的计算是一个经典的容斥,它等于无向图个数减去不连通的个数,不连通图的个数可以通过枚举“和节点 \(1\) 在同一个连通块的点数”来计算,具体地:

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

就是先确定哪 \(i-1\) 个点与 \(1\) 在同一个连通块中(组合数),然后连通部分的个数就是 \(f(i)\),剩下的部分随意连(即 \(g(n-i)\))。

把组合数展开得到

\[f(n) = g(n) - \sum_{i=1}^{n-1} \frac{(n-1)!}{(i-1)!(n-i)!} \cdot f(i) \cdot g(n-i) \\ f(n) = (n-1)! \cdot \left[ \frac{g(n)}{(n-1)!} - \sum_{i=1}^{n-1} \frac{f(i)}{(i-1)!} \cdot \frac{g(n-i)}{(n-i)!} \right] \\ \frac{f(n)}{(n-1)!} = \frac{g(n)}{(n-1)!} - \sum_{i=1}^{n-1} \frac{f(i)}{(i-1)!} \cdot \frac{g(n-i)}{(n-i)!} \]

那么定义 \(F(x), G(x), G_1(x)\) 如下:

\[F(x) = \sum_{i=1}^n \frac{f(i)}{(i-1)!} x^i \\ G(x) = \sum_{i=1}^n \frac{g(i)}{i!} x^i \\ G_1(x) = \sum_{i=1}^n \frac{g(i)}{(i-1)!} x^i \]

那么有

\[F(x) = G_1(x) - F(x) \cdot G(x) \\ F(x) = \frac{G_1(x)}{G(x) + 1} \]

最后输出时不要忘了把 \((n-1)!\) 乘回来!

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
#define rep(i, s, t) for(int i = (s), mi = (t); i <= mi; i++)
#define dwn(i, s, t) for(int i = (s), mi = (t); i >= mi; i--)

int read() {
	int x = 0, f = 1; char c = getchar();
	while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
	while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
	return x * f;
}

#define maxn 262144
#define MOD 1004535809
#define Groot 3
#define LL long long

int Pow(int a, int b) {
	int ans = 1, t = a;
	while(b) {
		if(b & 1) ans = (LL)ans * t % MOD;
		t = (LL)t * t % MOD; b >>= 1;
	}
	return ans;
}
int _Pow(int a, LL b) {
	int ans = 1, t = a;
	while(b) {
		if(b & 1) ans = (LL)ans * t % MOD;
		t = (LL)t * t % MOD; b >>= 1;
	}
	return ans;
}

int brev[maxn];
void FFT(int *a, int len, int tp) {
	int n = 1 << len;
	rep(i, 0, n - 1) if(i < brev[i]) swap(a[i], a[brev[i]]);
	rep(i, 1, len) {
		int wn = Pow(Groot, MOD - 1 >> i);
		if(tp < 0) wn = Pow(wn, MOD - 2);
		for(int j = 0; j < n; j += 1 << i) {
			int w = 1;
			rep(k, 0, (1 << i >> 1) - 1) {
				int la = a[j+k], ra = (LL)w * a[j+k+(1<<i>>1)] % MOD;
				a[j+k] = (la + ra) % MOD;
				a[j+k+(1<<i>>1)] = (la - ra + MOD) % MOD;
				w = (LL)w * wn % MOD;
			}
		}
	}
	if(tp < 0) {
		int invn = Pow(n, MOD - 2);
		rep(i, 0, n - 1) a[i] = (LL)a[i] * invn % MOD;
	}
	return ;
}

void Mul(int *A, int *B, int n, int m, bool recover = 0) {
	int N = 1, len = 0;
	while(N <= n + m) N <<= 1, len++;
	rep(i, 0, N - 1) brev[i] = (brev[i>>1] >> 1) | ((i & 1) << len >> 1);
	FFT(A, len, 1); FFT(B, len, 1);
	rep(i, 0, N - 1) A[i] = (LL)A[i] * B[i] % MOD;
	FFT(A, len, -1); if(recover) FFT(B, len, -1);
	return ;
}

int tmp[maxn];
void inverse(int *f, int *g, int n) {
	if(n == 1) return (void)(f[0] = Pow(g[0], MOD - 2));
	inverse(f, g, n + 1 >> 1);
	rep(i, 0, n - 1) tmp[i] = g[i];
	int N = 1, len = 0;
	while(N < (n << 1)) N <<= 1, len++;
	rep(i, 0, N - 1) brev[i] = (brev[i>>1] >> 1) | ((i & 1) << len >> 1);
	rep(i, n + 1 >> 1, N - 1) f[i] = 0;
	rep(i, n, N - 1) tmp[i] = 0;
	FFT(f, len, 1); FFT(tmp, len, 1);
	rep(i, 0, N - 1) f[i] = ((LL)f[i] * (2ll - (LL)tmp[i] * f[i] % MOD) % MOD + MOD) % MOD;
	FFT(f, len, -1);
	return ;
}

int fac[maxn], ifac[maxn];
void init(int n) {
	ifac[1] = 1;
	rep(i, 2, n) ifac[i] = (LL)(MOD - MOD / i) * ifac[MOD%i] % MOD;
	fac[0] = ifac[0] = 1;
	rep(i, 1, n) fac[i] = (LL)fac[i-1] * i % MOD, ifac[i] = (LL)ifac[i-1] * ifac[i] % MOD;
	return ;
}

LL C2(int n) { return (LL)n * (n - 1) >> 1; }

int G[maxn], G1[maxn], iG[maxn];

int main() {
	int n = read();
	init(n);
	
	rep(i, 1, n) {
		int t = _Pow(2, C2(i));
		G[i] = (LL)t * ifac[i] % MOD;
		G1[i] = (LL)t * ifac[i-1] % MOD;
	}
	G[0] = 1;
	inverse(iG, G, n + 1);
	Mul(G1, iG, n, n);
	
	printf("%lld\n", (LL)G1[n] * fac[n-1] % MOD);
	
	return 0;
}
posted @ 2018-02-21 10:40  xjr01  阅读(386)  评论(0编辑  收藏  举报