城市规划
题目链接:luogu P4841
题目大意
给你 n,要你求 n 个点简单有标号无向连通图的个数。
思路
设 g i g_i gi 为 n n n 个点的简单有标号图个数, f i f_i fi 则是满足条件的。
g i = 2 ( i 2 ) g_i=2^{\binom{i}{2}} gi=2(2i)
f n = g n − ∑ i = 1 n − 1 ( n − 1 i − 1 ) f i g n − i f_n=g_n-\sum\limits_{i=1}^{n-1}\binom{n-1}{i-1}f_ig_{n-i} fn=gn−i=1∑n−1(i−1n−1)fign−i
g n = ∑ i = 1 n ( n − 1 i − 1 ) f i g n − i g_n=\sum\limits_{i=1}^n\binom{n-1}{i-1}f_ig_{n-i} gn=i=1∑n(i−1n−1)fign−i
g n = ∑ i = 1 n ( n − 1 ) ! ( i − 1 ) ! ( n − i ) f i g n − i g_n=\sum\limits_{i=1}^n\dfrac{(n-1)!}{(i-1)!(n-i)}f_ig_{n-i} gn=i=1∑n(i−1)!(n−i)(n−1)!fign−i
g n ( n − 1 ) ! = ∑ i = 1 n f i ( i − 1 ) ! g n − i ( n − i ) ! \dfrac{g_n}{(n-1)!}=\sum\limits_{i=1}^n\dfrac{f_i}{(i-1)!}\dfrac{g_{n-i}}{(n-i)!} (n−1)!gn=i=1∑n(i−1)!fi(n−i)!gn−i
然后不难看出可以看成三个多项式,用个多项式求逆加多项式乘法即可。
代码
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
#define mo 1004535809
#define cpy(f, g, x) memcpy(f, g, sizeof(int) * (x))
#define clr(f, x) memset(f, 0, sizeof(int) * (x))
using namespace std;
const int N = 130000 * 8 + 5;
int n, g[N], g1[N], g2[N];
int jc[N], inv[N], invs[N];
int add(int x, int y) {return x + y >= mo ? x + y - mo : x + y;}
int dec(int x, int y) {return x < y ? x - y + mo : x - y;}
int mul(int x, int y) {return 1ll * x * y % mo;}
int ksm(int x, int y) {int re = 1; while (y) {if (y & 1) re = mul(re, x); x = mul(x, x); y >>= 1;} return re;}
struct Poly {
int G, Gv, an[N];
vector <int> Wn[21], Wn_[21];
void Init() {
G = 3; Gv = ksm(G, mo - 2);
for (int mid = 1, d = 0; mid < N; mid <<= 1, d++) {
int w = ksm(G, (mo - 1) / (mid << 1)), wv = ksm(Gv, (mo - 1) / (mid << 1));
for (int j = 0, wn = 1, wn_ = 1; j < mid; j++, wn = mul(wn, w), wn_ = mul(wn_, wv)) {
Wn[d].push_back(wn); Wn_[d].push_back(wn_);
}
}
}
void get_an(int limit, int l_size) {
for (int i = 0; i < limit; i++) an[i] = (an[i >> 1] >> 1) | ((i & 1) << (l_size - 1));
}
void NTT(int *f, int limit, int op) {
for (int i = 0; i < limit; i++) if (i < an[i]) swap(f[i], f[an[i]]);
for (int mid = 1, d = 0; mid < limit; mid <<= 1, d++)
for (int R = mid << 1, j = 0; j < limit; j += R)
for (int k = 0; k < mid; k++) {
int x = f[j | k], y = mul(f[j | mid | k], (op == 1) ? Wn[d][k] : Wn_[d][k]);
f[j | k] = add(x, y); f[j | mid | k] = dec(x, y);
}
if (op == -1) {
int limv = ksm(limit, mo - 2);
for (int i = 0; i < limit; i++) f[i] = mul(f[i], limv);
}
}
void px(int *f, int *g, int limit) {
for (int i = 0; i < limit; i++) f[i] = mul(f[i], g[i]);
}
void times(int *f, int *g, int n, int m, int T) {
static int tmp[N];
int limit = 1, l_size = 0; while (limit < n + m) limit <<= 1, l_size++;
get_an(limit, l_size);
clr(f + n, limit - n); cpy(tmp, g, m); clr(tmp + m, limit - m);
NTT(f, limit, 1); NTT(tmp, limit, 1); px(f, tmp, limit); NTT(f, limit, -1);
clr(f + T, limit - T); clr(tmp, limit);
}
void invp(int *f, int n) {
static int w[N], r[N], tmp[N];
w[0] = ksm(f[0], mo - 2);
int limit = 1, l_size = 0;
for (int len = 2; (len >> 1) <= n; len <<= 1) {
limit = len; l_size++; get_an(limit, l_size);
cpy(r, w, len >> 1);
cpy(tmp, f, limit); NTT(tmp, limit, 1);
NTT(r, limit, 1); px(r, tmp, limit);
NTT(r, limit, -1); clr(r, limit >> 1);
cpy(tmp, w, len); NTT(tmp, limit, 1);
NTT(r, limit, 1); px(r, tmp, limit);
NTT(r, limit, -1);
for (int i = (len >> 1); i < len; i++)
w[i] = dec(mul(w[i], 2), r[i]);
}
cpy(f, w, n); clr(w, limit); clr(r, limit); clr(tmp, limit);
}
}P;
void Init() {
P.Init();
jc[0] = 1; for (int i = 1; i < N; i++) jc[i] = mul(jc[i - 1], i);
inv[0] = inv[1] = 1; for (int i = 2; i < N; i++) inv[i] = mul(inv[mo % i], mo - mo / i);
invs[0] = 1; for (int i = 1; i < N; i++) invs[i] = mul(invs[i - 1], inv[i]);
}
int main() {
Init();
scanf("%d", &n);
for (int i = 0; i <= n; i++) g[i] = ksm(2, 1ll * i * (i - 1) / 2 % (mo - 1));
for (int i = 1; i <= n; i++) g1[i] = mul(g[i], invs[i - 1]);
for (int i = 0; i <= n; i++) g2[i] = mul(g[i], invs[i]);
P.invp(g2, n + 1); P.times(g1, g2, n + 1, n + 1, n + 1);
printf("%d", mul(g1[n], jc[n - 1]));
return 0;
}