NOI 模擬賽 T3
(前兩題屬於“很簡單,但不好描述”
這題屬於“很難,但可以描述”
求所有 $n$ 個點的無向連通圖的邊數的 $m$ 次方和,膜 $998244353$
$n \leq 50000,m \leq 15$
sol:
考慮先把這個 $m$ 次方轉化一下:$|E|^m = \sum\limits_{i=0}^k \binom{|E|}{i} \times Stirling2(m,i) \times i!$
然後考慮 $m$ 次方和的意義:一次方就是枚舉一條邊,計算強制選它的方案數,平方就是枚舉兩條邊,計算強制包含它們的方案數...$m$ 次方就是枚舉 $m$ 條邊,計算強制包含它們的方案數。
然後我們可以用 $f_n(i)$ 表示 $n$ 個點的所有連通圖中選出 $i$ 個邊的方案數,顯然這個跟計算“強制包含給定的 $i$ 條邊的方案數”是等價的,因為你“給定 $i$ 條邊”是對於原圖邊集的所有子集來說的
這個可以延續 $f_n(0)$ (可以發現這個就是 bzoj 3456 城市規劃)的求法,設一個 $g_n(i)$ 表示不一定連通的方案數,然後枚舉 $1$ 號點所在的連通塊大小來容斥
可以得到這樣一個式子:$g_m(n) = \sum\limits_{i=1}^n \sum\limits_{j=1}^m \binom{n-1}{i-1} f_j(i) \times g_{m-j}(n-i)$、
乍一看這是一個二維卷積,但第二維好像只有 15 ,所以可以直接做
這個式子可以兩邊同時除以 $(n-1)!$ 來轉化成分治 FFT 的標準形式
然後眾所周知,那個也是多項式求逆的標準形式,可以把已經能算的東西都放到等式的一邊,然後另一邊剩下一個 $F_m \times G_0$ 多項式求逆即可
復雜度大概是 $O(nm^2logn)$
#include <bits/stdc++.h> #define LL long long #define rep(i, s, t) for (register int i = (s), i##end = (t); i <= i##end; ++i) #defineView Codedwn(i, s, t) for (register int i = (s), i##end = (t); i >= i##end; --i) using namespace std; inline int read() { int x = 0,f = 1; char ch = getchar(); for(; !isdigit(ch); ch = getchar())if(ch == ‘-‘) f = -f; for(; isdigit(ch); ch = getchar())x = 10 * x + ch - ‘0‘; return x * f; } const int maxn = 200010, mod = 998244353; inline int inc(int x, int y) {x += y; if(x >= mod) x -= mod; return x;} inline int dec(int x, int y) {x -= y; if(x < 0) x += mod; return x;} inline int mul(int x, int y) {return 1LL * x * y % mod;} int lg[maxn], r[maxn], wn[(1 << 19) + 10], iwn[(1 << 19) + 10], fac[maxn], ifac[maxn], inv[maxn], temp[maxn]; int n, m, F[17][maxn], G[17][maxn], H[17][maxn], s[20][20], iv[maxn], cd[maxn]; inline int ksm(int x, LL t, int res = 1) { for(; t; x = mul(x, x), t >>= 1) if(t & 1) res = mul(res, x); return res; } void init_fac() { lg[0] = -1; rep(i, 1, maxn - 10) lg[i] = lg[i >> 1] + 1; fac[0] = ifac[0] = 1; rep(i, 1, maxn - 10) fac[i] = mul(fac[i - 1], i); ifac[maxn - 10] = ksm(fac[maxn - 10], mod - 2); dwn(i, maxn - 11, 1) ifac[i] = mul(ifac[i + 1], i + 1); inv[0] = inv[1] = 1; rep(i, 2, maxn - 10) inv[i] = mul((mod - mod / i), inv[mod % i]); int lim = (1 << 19); wn[0] = iwn[0] = 1; rep(i, 1, lim-1) wn[i] = ksm(3, (mod - 1) / (i << 1)), iwn[i] = ksm(332748118, (mod - 1) / (i << 1)); s[0][0] = 1; rep(i, 1, m) rep(j, 1, i) s[i][j] = inc(s[i - 1][j - 1], mul(j, s[i - 1][j])); } void fft(int *a, int n, int f) { rep(i, 0, n-1) r[i] = (r[i >> 1] >> 1) | ((i & 1) << (lg[n] - 1)); rep(i, 0, n-1) if(i < r[i]) swap(a[i], a[r[i]]); for(register int i = 1; i < n; i <<= 1) { int now = (~f) ? wn[i] : iwn[i]; for(register int j = 0; j < n; j += (i << 1)) { int w = 1; for(register int k = 0; k < i; ++k, w = mul(w, now)) { int x = a[j + k], y = mul(w, a[j + k + i]); a[j + k] = inc(x, y); a[j + k + i] = dec(x, y); } } } if(f == -1) rep(i, 0, n-1) a[i] = mul(a[i], inv[n]); } inline void Inverse(int *a, int *b, int n) { if(n == 1) {b[0] = ksm(a[0],mod - 2); return;} Inverse(a,b,n >> 1); memcpy(temp,a,n * sizeof(int)); memset(temp + n,0,n * sizeof(int)); fft(temp,n << 1,1); fft(b,n << 1,1); for(int i=0; i<(n << 1); i++)b[i] = 1LL * b[i] * ((2LL - 1LL * temp[i] * b[i] % mod + mod) % mod) % mod; fft(b,n << 1,-1); memset(b + n,0,n * sizeof(int)); } int C(LL x, int y) { if(!y || y == x) return 1; int now = ifac[y]; dwn(i, x, x-y+1) now = mul(now, i); return now; } int main() { n = read(), m = read(); init_fac(); int len = 1, wlen; for(; len <= n; len <<= 1); wlen = (len << 1); rep(cur, 0, m) { rep(i, 0, n) { LL M = 1LL * i * (i - 1) / 2; if(M < (LL)cur) continue; G[cur][i] = mul(C(M, cur), ksm(2, M - cur)); } rep(i, 1, n) H[cur][i] = mul(G[cur][i], ifac[i - 1]); rep(i, 0, n) G[cur][i] = mul(G[cur][i], ifac[i]); if(cur == 0) Inverse(G[cur], iv, len); fft(H[cur], wlen, 1); fft(G[cur], wlen, 1); } fft(iv, wlen, 1); rep(cur, 0, m) { memcpy(cd, H[cur], sizeof(int) * wlen); rep(pcur, 1, cur) rep(i, 0, wlen) cd[i] = dec(cd[i], mul(F[cur - pcur][i], G[pcur][i])); fft(cd, wlen, -1); memset(cd + len, 0, sizeof(int) * len); fft(cd, wlen, 1); rep(i, 0, wlen) cd[i] = mul(cd[i], iv[i]); fft(cd, wlen, -1); memset(cd + len, 0, sizeof(int) * len); fft(cd, wlen, 1); memcpy(F[cur], cd, sizeof(int) * wlen); } rep(cur, 0, m) fft(F[cur], wlen, -1), F[cur][n] = mul(F[cur][n], fac[n - 1]); int ans = 0; rep(i, 0, m) ans = inc(ans, mul(mul(s[m][i], fac[i]), F[i][n])); cout << ans << endl; }
NOI 模擬賽 T3