1. 程式人生 > >Luogu 4841 城市規劃

Luogu 4841 城市規劃

BZOJ 3456 許可權題

太菜了推不出式子

我們設$f(n)$表示$n$個點的無向連通圖的數量,那麼有

$$f(n) = 2^{\binom{n}{2}} - \sum_{i = 1}^{n - 1}\binom{n - 1}{i - 1}f(i)2^{\binom{n - i}{2}}$$

思路就是全部減去不合法的,列舉$1$號點所在的聯通塊的大小,剩下隨便生成一張無向圖。

拆開組合數,簡單變形一下

$$f(n) = 2^{\binom{n}{2}} - (n - 1)!\sum_{i = 1}^{n - 1}\frac{2^{\binom{n - i}{2}}}{(n - i)!}\frac{f(i)}{(i - 1)!}$$

記$h(i) = \frac{f(i)}{(i - 1)!}$,$g(i) = \frac{2^{\binom{i}{2}}}{i!}$

$$(n - 1)!h(n) = 2^{\binom{n}{2}} - (n - 1)!\sum_{i = 1}^{n - 1}g(i)h(n - i)$$

發現這個式子可以用分治FFT優化,直接上就做完了。

時間複雜度$O(nlog^2n)$。

Code:

#include <cstdio>
#include <cstring>
using namespace std;
typedef long
long ll; const int N = 3e5 + 5; const ll P = 1004535809LL; int n, lim, pos[N]; ll f[N], g[N], fac[N], inv[N], a[N], b[N]; template <typename T> inline void read(T &X) { X = 0; char ch = 0; T op = 1; for (; ch > '9'|| ch < '0'; ch = getchar()) if (ch == '-') op = -1
; for (; ch >= '0' && ch <= '9'; ch = getchar()) X = (X << 3) + (X << 1) + ch - 48; X *= op; } inline ll fpow(ll x, ll y) { ll res = 1; for (; y > 0; y >>= 1) { if (y & 1) res = res * x % P; x = x * x % P; } return res; } template <typename T> inline void swap(T &x, T &y) { T t = x; x = y; y = t; } inline void prework(int len) { int l = 0; for (lim = 1; lim <= len; lim <<= 1, ++l); for (int i = 0; i < lim; i++) pos[i] = (pos[i >> 1] >> 1) | ((i & 1) << (l - 1)); } inline void ntt(ll *c, int opt) { for (int i = 0; i < lim; i++) if (i < pos[i]) swap(c[i], c[pos[i]]); for (int i = 1; i < lim; i <<= 1) { ll wn = fpow(3, (P - 1) / (i << 1)); if (opt == -1) wn = fpow(wn, P - 2); for (int len = i << 1, j = 0; j < lim; j += len) { ll w = 1; for (int k = 0; k < i; k++, w = w * wn % P) { ll x = c[j + k], y = w * c[j + k + i] % P; c[j + k] = (x + y) % P, c[j + k + i] = (x - y + P) % P; } } } if (opt == -1) { ll invP = fpow(lim, P - 2); for (int i = 0; i < lim; i++) c[i] = c[i] * invP % P; } } inline ll getC(int x) { if (x < 2) return 0; return (1LL * x * (x - 1) / 2); } void solve(int l, int r) { if (l == r) { f[l] = (fpow(2, getC(l) % (P - 1)) - f[l] * fac[l - 1] % P + P) % P; f[l] = f[l] * inv[l - 1] % P; return; } int mid = ((l + r) >> 1); solve(l, mid); prework(r - l + 1); for (int i = 0; i < lim; i++) a[i] = b[i] = 0LL; for (int i = l; i <= mid; i++) a[i - l] = f[i]; for (int i = 1; i <= r - l; i++) b[i - 1] = g[i]; ntt(a, 1), ntt(b, 1); for (int i = 0; i < lim; i++) a[i] = a[i] * b[i] % P; ntt(a, -1); for (int i = mid + 1; i <= r; i++) f[i] = (f[i] + a[i - l - 1] % P) % P; solve(mid + 1, r); } int main() { read(n); fac[0] = 1LL; for (int i = 1; i <= n; i++) fac[i] = fac[i - 1] * i % P; inv[n] = fpow(fac[n], P - 2); for (int i = n - 1; i >= 0; i--) inv[i] = inv[i + 1] * (i + 1) % P; for (int i = 1; i <= n; i++) g[i] = fpow(2LL, getC(i) % (P - 1)) * inv[i] % P; solve(1, n); printf("%lld\n", f[n] * fac[n - 1] % P); return 0; }
View Code