[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;
}