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)
#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;
}
View Code

猜你喜欢

转载自www.cnblogs.com/Kong-Ruo/p/10601256.html