题解:P5219 无聊的水题 I
如果你不会 FFT/NTT 等多项式算法,这篇题解可能会帮到你。
题意:求有
n 个节点,且节点的最大度数恰好为m 的不同树的数量。
直接求最大度数恰好为
根据 Prufer 序列的性质,若节点
要从
因为有
我们最终需要的合法 Prufer 序列的数量,就是
:::success[代码]
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
using V = vector<LL>;
const int N = 2e5 + 5, M = 998244353;
LL n, m, r[N], f[N];
LL P(LL x, LL y) {
LL s = 1;
for (; y; x = x * x % M, y >>= 1) y & 1 && (s = s * x % M);
return s;
}
void NTT(V &a, int p, int k) {
for (int i = 0; i < k; i++) {
i < r[i] && (swap(a[i], a[r[i]]), 1);
}
for (int d = 1; d < k; d <<= 1) {
LL wn = P(~p ? 3 : (M + 1) / 3, (M - 1) / (d << 1));
for (int i = 0; i < k; i += (d << 1)) {
LL w = 1;
for (int j = 0; j < d; j++, w = w * wn % M) {
LL x = a[i + j], y = a[i + j + d];
a[i + j] = (x + w * y % M) % M;
a[i + j + d] = (x - w * y % M + M) % M;
}
}
}
}
V X(V a, V b) {
int n = a.size() - 1, m = b.size() - 1, k, l = 0;
for (k = 1; k <= n + m; k *= 2, l++);
for (int i = 0; i < k; i++) {
r[i] = ((r[i >> 1] >> 1) | ((i & 1) << l - 1));
}
a.resize(k), b.resize(k);
NTT(a, 1, k), NTT(b, 1, k);
V c(k);
for (int i = 0; i < k; i++) c[i] = a[i] * b[i] % M;
NTT(c, -1, k);
for (LL &x : c) x = x * P(k, M - 2) % M;
c.resize(::n - 1);
return c;
}
V P(V x, LL y) {
V s(1, 1);
for (; y; x = X(x, x), y >>= 1) y & 1 && (s = X(s, x), 1);
return s;
}
LL C(LL p) {
LL k = p - 1, v = 1;
V a;
for (int i = 0; i <= k; i++) a.push_back(P(f[i], M - 2));
return f[n - 2] * P(a, n)[n - 2] % M;
}
int main() {
cin.tie(0)->sync_with_stdio(0);
cin >> n >> m;
for (int i = f[0] = 1; i <= n; i++) f[i] = f[i - 1] * i % M;
cout << (C(m) - C(m - 1) + M) % M;
return 0;
}
:::
这里还有一种不用多项式算法的做法:
考虑对
对两边同时求导,得到
移个项,得到
设
对等式两边同时提取
- 左边:
[x ^ {i - 1}](G'(x) \cdot F(x)) = \sum_{j = 0}^{\min(i - 1, k)} (i - j) f_j g_{i - j} = i \cdot g_i - \sum_{j = 1}^{\min(i, k)} (i - j) f_j g_{i - j} - 右边:
[x ^ {i - 1}](n \cdot G(x) \cdot F'(x)) = n \sum_{j = 1}^{\min(i, k)} j \cdot f_j g_{i - j}
两边系数必然相等,得到等式
整理可得
预处理一下阶乘和逆元即可做到
:::success[代码]
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const int N = 5e4 + 5, M = 998244353;
LL n, m, f[N], g[N];
LL P(LL x, LL y) {
LL s = 1;
for (; y; x = x * x % M, y >>= 1) y & 1 && (s = s * x % M);
return s;
}
LL C(LL p) {
LL k = p - 1, v = 1;
for (LL i = g[0] = 1; i <= n - 2; i++) {
g[i] = 0;
for (LL j = 1; j <= min(i, k); j++) {
g[i] = (g[i] + (j * (n + 1) - i + M) % M * f[j] % M * g[i - j] % M) % M;
}
g[i] = g[i] * P(i, M - 2) % M, v = v * i % M;
}
return v * g[n - 2] % M;
}
int main() {
cin.tie(0)->sync_with_stdio(0);
cin >> n >> m;
for (int i = f[0] = 1; i <= n; i++) f[i] = f[i - 1] * P(i, M - 2) % M;
cout << (C(m) - C(m - 1) + M) % M;
return 0;
}
:::
感谢 @apple365 老师的指导。