Luogu 5219 无聊的水题 I

发布时间 2023-07-21 15:47:26作者: Ender_32k

小清新 prufer 序列。

给定 \(n,m\),求 \(n\) 个点且最大度数为 \(m\) 的有标号无根树个数。

看到度数,不难想到 prufer 序列。

众所周知,prufer 序列给出了长度为 \(n-2\) 值域为 \(n\) 的序列与带标号无根树的双射。某个点的度数为 \(d_u\),那么 \(u\) 在 prufer 序列中出现了 \(d_u-1\) 次。

所以题目就转化成:

给定 \(n,m\),求长度为 \(n-2\),值域为 \(n\) 的序列,使得出现次数最多的数恰出现了 \(m-1\) 次。

考虑“恰好”的限制是困难的,转化为所有数出现次数不超过 \(m-1\) 次的答案减去出现次数不超过 \(m-2\) 次的答案。

现在考虑求出所有点出现次数不超过 \(m-1\) 次的答案。

此时值域 \([1,n]\) 相当于 \(n\) 种颜色,我们考虑对颜色单独计数,然后将它们的 EGF 相乘得到颜色混合的答案。显然一种颜色不能超过 \(m-1\) 个相当于次数的限制,那么:

\[F(x)=\sum\limits_{i=0}^{m-1}\frac{x_i}{i!} \]

这是一种颜色的 EGF(为什么是 EGF?是因为我们对于序列计数,颜色之间的顺序是有关系的)。那么 \(n\) 种颜色的 EGF 卷一下,第 \(n-2\) 项的系数即为所求:

\[\text{ans}=(n-2)![x^{n-2}]F^n(x) \]

倍增快速幂即可。复杂度 \(O(n\log^2n)\)

// Problem: P5219 无聊的水题 I
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P5219
// Memory Limit: 500 MB
// Time Limit: 6000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
using namespace std;

namespace vbzIO {
    char ibuf[(1 << 20) + 1], *iS, *iT;
    #if ONLINE_JUDGE
    #define gh() (iS == iT ? iT = (iS = ibuf) + fread(ibuf, 1, (1 << 20) + 1, stdin), (iS == iT ? EOF : *iS++) : *iS++)
    #else
    #define gh() getchar()
    #endif
    #define mt make_tuple
    #define mp make_pair
    #define fi first
    #define se second
    #define pc putchar
    #define pb emplace_back
    #define ins insert
    #define era erase
    typedef tuple<int, int, int> tu3;
    typedef pair<int, int> pi;
    inline int rd() {
        char ch = gh();
        int x = 0;
        bool t = 0;
        while (ch < '0' || ch > '9') t |= ch == '-', ch = gh();
        while (ch >= '0' && ch <= '9') x = (x << 1) + (x << 3) + (ch ^ 48), ch = gh();
        return t ? ~(x - 1) : x;
    }
    inline void wr(int x) {
        if (x < 0) x = ~(x - 1), putchar('-');
        if (x > 9) wr(x / 10);
        putchar(x % 10 + '0');
    }
}
using namespace vbzIO;

const int N = 1e6 + 100;
const int P = 998244353;
const int G = 114514;
int len, lim, f[N], g[N], tr[N], fac[N], ifac[N], inv[N];

int qpow(int p, int q) {
	int res = 1;
	for (; q; q >>= 1, p = 1ll * p * p % P)
		if (q & 1) res = 1ll * res * p % P;
	return res;
}

const int iG = qpow(G, P - 2);

void init(int lim) {
	fac[0] = ifac[0] = inv[1] = 1;
	for (int i = 1; i <= lim; i++) {
		if (i > 1) inv[i] = 1ll * inv[P % i] * (P - P / i) % P;
		fac[i] = 1ll * fac[i - 1] * i % P;
		ifac[i] = 1ll * ifac[i - 1] * inv[i] % P;
	}
}

void NTT(int *f, int op) {
	for (int i = 0; i < len; i++)
		if (i < tr[i]) swap(f[i], f[tr[i]]);
	for (int o = 2, k = 1; k < len; o <<= 1, k <<= 1) {
		int tg = qpow(~op ? G : iG, (P - 1) / o);
		for (int i = 0; i < len; i += o) {
			for (int j = 0, w = 1; j < k; j++, w = 1ll * w * tg % P) {
				int x = f[i + j], y = 1ll * w * f[i + j + k] % P;
				f[i + j] = (x + y) % P, f[i + j + k] = (x - y + P) % P;
			}
		}
	}
	if (~op) return;
	int iv = qpow(len, P - 2);
	for (int i = 0; i < len; i++)
		f[i] = 1ll * f[i] * iv % P;
}

void Mul(int n, int *f, int *g, int *h) {
	static int tf[N], tg[N];
	len = 1, lim = 0;
	while (len <= ((n - 1) << 1)) len <<= 1, lim++;
	for (int i = 1; i < len; i++)
		tr[i] = (tr[i >> 1] >> 1) | ((i & 1) << (lim - 1));
	for (int i = 0; i <= n - 2; i++) 
		tf[i] = f[i], tg[i] = g[i];
	for (int i = n - 1; i < len; i++)
		tf[i] = tg[i] = 0;
	NTT(tf, 1), NTT(tg, 1);
	for (int i = 0; i < len; i++)
		tf[i] = 1ll * tf[i] * tg[i] % P;
	NTT(tf, -1);
	for (int i = 0; i <= n - 2; i++)
		h[i] = tf[i];
}

int calc(int n, int m) {
	memset(f, 0, sizeof(f));
	for (int i = 0; i <= min(m - 1, n - 2); i++) 
		f[i] = ifac[i];
	memset(g, 0, sizeof(g)), g[0] = 1;
	int tp = n;
	for (; tp; tp >>= 1, Mul(n, f, f, f))
		if (tp & 1) Mul(n, g, f, g);
	return 1ll * fac[n - 2] * g[n - 2] % P;
}

int main() {
    int n = rd(), m = rd();
    init(max(n, m));
    wr((calc(n, m) - calc(n, m - 1) + P) % P); 
    return 0;
}