(前两题属于“很简单,但不好描述”
这题属于“很难,但可以描述”
求所有 $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) #define dwn(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; }