Luogu 5219 无聊的水题 I
小清新 prufer 序列。
给定 ,求 个点且最大度数为 的有标号无根树个数。
看到度数,不难想到 prufer 序列。
众所周知,prufer 序列给出了长度为 值域为 的序列与带标号无根树的双射。某个点的度数为 ,那么 在 prufer 序列中出现了 次。
所以题目就转化成:
给定 ,求长度为 ,值域为 的序列,使得出现次数最多的数恰出现了 次。
考虑“恰好”的限制是困难的,转化为所有数出现次数不超过 次的答案减去出现次数不超过 次的答案。
现在考虑求出所有点出现次数不超过 次的答案。
此时值域 相当于 种颜色,我们考虑对颜色单独计数,然后将它们的 EGF 相乘得到颜色混合的答案。显然一种颜色不能超过 个相当于次数的限制,那么:
这是一种颜色的 EGF(为什么是 EGF?是因为我们对于序列计数,颜色之间的顺序是有关系的)。那么 种颜色的 EGF 卷一下,第 项的系数即为所求:
倍增快速幂即可。复杂度 。
// 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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现