Luogu P7728. 旧神归来(Return of Them)
有一棵树,生长规则如下:
- 初始有一棵包含 \(n\) 个结点的以 \(1\) 为根的有根树 \(T_0\);
- 在第 \(i\) 天,树将从 \(T_{i - 1}\) 生长为 \(T_i\),生长规则为:令 \(v\) 是 \(T_{i - 1}\) 中深度最小的叶结点(若有多个则任意选择一个),以 \(v\) 这个点替换成 \(T_{i - 1}\) 本身。
需要计算:
- 对于每个整数 \(d \in [1, m]\),求出 \(S_d\) 表示最小的天数,满足 \(T_{S_d}\) 中深度最小的叶结点深度大于 \(d\)。
答案对 \(998244353\) 取模。
\(2 \le n, m \le {10}^5\)。
考虑操作只与叶子节点组成的深度可重集合有关,设 \(S\) 为所有叶子节点组成的深度可重集合,观察一次操作前后的变化:
- 设替换的叶子节点的深度为 \(d\),那么少了一个 \(d\),多了一族 \(S+d\)。
尝试用生成函数表达这一过程,设 \(F\) 为 \(S\) 的 \(\text{OGF}\)。一次操作即为:
\[F(x)\leftarrow (1+x^d)F(x)-x^d
\]
设深度为 \(i\) 的点操作了 \(a_i\) 次,推导可得
\[\prod_{i\ge1} (1+x^i)^{a_i}(F(x)-1)=-1
\]
则
\[\prod_{i\ge1} (1+x^i)^{a_i}=\frac{1}{1-F(x)}
\]
两边同时取 \(\ln\)
\[\sum_{i\ge1} a_i\ln (1+x^i)=\ln \frac{1}{1-F(x)}
\]
\[\begin{aligned}\ln(1-x^n)&=\int \frac{\text{d}}{\text{d}x}\ln(1-x^n)=-\int\frac{nx^{n-1}}{1-x^n}\\ &=-\int \sum_{t\ge 0}nx^{(t+1)n-1}=-\int \sum_{t\ge1}nx^{tn-1}=-\sum_{t\ge 1}\frac{x^{tn}}{t}\\ \end{aligned} \]
根据这个公式,将 \(-x^i\) 代入,得
\[\sum_{ij=n} \frac{(-1)^{j-1}a_i}{j}=[x^n]\frac{1}{1-F(x)}
\]
先 \(O(n\log n)\) 求出 \(\frac{1}{1-F(x)}\),然后再 \(O(n\log n)\) 解出 \(a_i\) 即可。
#include <bits/stdc++.h>
#define eb emplace_back
using namespace std;
typedef long long ll;
typedef vector<int> Poly;
const int N = 1 << 20, mod = 998244353, inv2 = mod + 1 >> 1;
int n, m, sum, r[N], ans[N], dep[N], Inv[N];
vector<int> G[100005]; Poly S;
inline int add(int x, int y) { return x + y >= mod ? x + y - mod : x + y; }
inline int dec(int x, int y) { return x - y < 0 ? x - y + mod : x - y; }
inline int mul(int x, int y) { return (ll)x * y % mod; }
inline int qpow(int a, int b) {
int ans = 1;
for (; b; b >>= 1, a = mul(a, a)) if (b & 1) ans = mul(ans, a);
return ans;
}
inline void NTT(Poly &A, int len, int type){
for (int i = 0; i < len; ++ i) if(i < r[i]) swap(A[i], A[r[i]]);
for (int mid = 1; mid < len; mid <<= 1) {
int Wn = qpow(type == 1 ? (mod + 1) / 3 : 3, (mod - 1) / (mid << 1));
for (int j = 0; j < len; j += (mid << 1))
for (int k = 0, w = 1; k < mid; ++ k, w = mul(w, Wn)) {
int x = A[j + k], y = mul(w, A[j + k + mid]);
A[j + k] = add(x, y), A[j + k + mid] = dec(x, y);
}
}
if (type > 0) return;
for (int i = 0; i < len; ++ i) A[i] = mul(A[i], Inv[len]);
}
inline void init_rev(int len) {
for (int i = 0; i < len; ++ i) r[i] = r[i >> 1] >> 1 | ((i & 1) * (len >> 1));
}
inline Poly operator + (const Poly&a, const Poly&b) {
Poly c = a; c.resize(max(a.size(), b.size()));
for (int i = 0; i < b.size(); ++ i) c[i] = add(c[i], b[i]);
return c;
}
inline Poly operator - (const Poly&a, const Poly&b) {
Poly c = a; c.resize(max(a.size(), b.size()));
for (int i = 0; i < b.size(); ++ i) c[i] = dec(c[i], b[i]);
return c;
}
inline Poly operator * (Poly a, Poly b) {
int n = a.size(), m = b.size(), l = 1;
while (l < n + m - 1) l <<= 1;
init_rev(l);
a.resize(l), NTT(a, l, 1);
b.resize(l), NTT(b, l, 1);
for (int i = 0; i < l; ++ i) a[i] = mul(a[i], b[i]);
NTT(a, l, -1);
a.resize(n + m - 1);
return a;
}
inline Poly operator * (Poly a, int b) {
for (int i = 0; i < a.size(); ++ i) a[i] = mul(a[i], b);
return a;
}
inline Poly Deriv(Poly a) {
for (int i = 0; i + 1 < a.size(); ++ i) a[i] = mul(a[i + 1], i + 1);
return a.pop_back(), a;
}
inline Poly Integ(Poly a) {
a.emplace_back(0);
for (int i = a.size() - 1; i; -- i) a[i] = mul(a[i - 1], Inv[i]);
return a[0] = 0, a;
}
inline Poly Polyinv(const Poly&a, int lim) {
Poly c, b(1, qpow(a[0], mod - 2));
for (int l = 4; (l >> 2) < lim; l <<= 1) {
init_rev(l);
c = a, c.resize(l >> 1);
c.resize(l), NTT(c, l, 1);
b.resize(l), NTT(b, l, 1);
for (int i = 0; i < l; ++ i) b[i] = mul(b[i], dec(2, mul(b[i], c[i])));
NTT(b, l, -1), b.resize(l >> 1);
}
return b.resize(lim), b;
}
inline Poly Polyinv(const Poly&a) { return Polyinv(a, a.size()); }
inline Poly Polyln(Poly a, int lim) {
a = Integ(Deriv(a) * Polyinv(a, lim));
return a.resize(lim), a;
}
inline Poly Polyln(const Poly&a) { return Polyln(a, a.size()); }
inline Poly Polyexp(const Poly&a, int lim) {
Poly c, b(1, 1); int n = a.size();
for (int i = 2; (i >> 1) < lim; i <<= 1) {
c = Polyln(b, i);
for (int j = 0; j < i; ++ j) c[j] = dec(j < n ? a[j] : 0, c[j]);
c[0] = add(c[0], 1);
b = b * c, b.resize(i);
}
return b.resize(lim), b;
}
inline Poly Polyexp(const Poly&a) { return Polyexp(a, a.size()); }
inline Poly Polysqrt(const Poly&a, int lim) {
Poly c, d, b(1, 1);
for (int l = 4; (l >> 2) < lim; l <<= 1) {
init_rev(l);
c = a, c.resize(l >> 1);
d = Polyinv(b, l >> 1);
c.resize(l), NTT(c, l, 1);
d.resize(l), NTT(d, l, 1);
for (int j = 0; j < l; ++ j) c[j] = mul(c[j], d[j]);
NTT(c, l, -1), b.resize(l >> 1);
for (int j = 0; j < (l >> 1); ++ j) b[j] = mul(add(b[j], c[j]), inv2);
}
return b.resize(lim), b;
}
inline Poly Polysqrt(const Poly&a) { return Polysqrt(a, a.size()); }
inline Poly Polypow(Poly a, int k, int lim) {
a = Polyexp(Polyln(a) * k);
return a.resize(lim), a;
}
inline Poly Polypow(const Poly&a, int k) { return Polypow(a, k, a.size()); }
inline void precalc() {
Inv[0] = Inv[1] = 1;
for (int i = 2; i < N; ++ i) Inv[i] = mul(mod - mod / i, Inv[mod % i]);
}
inline void dfs(int x, int fa) {
int fl = 0;
for (auto y : G[x]) if (y ^ fa)
dep[y] = dep[x] + 1, dfs(y, x), fl = 1;
if (!fl) S[dep[x]] = dec(S[dep[x]], 1);
}
int main() {
precalc();
scanf("%d%d", &n, &m);
for (int i = 1, x, y; i < n; ++ i) {
scanf("%d%d", &x, &y);
G[x].eb(y), G[y].eb(x);
}
S.resize(max(n, m) + 5);
S[0] = 1;
dfs(1, 0);
Poly T = Polyln(S);
for (int i = 1; i <= m; ++ i) {
ans[i] = dec(ans[i], T[i]);
for (int j = 2; j <= m / i; ++ j)
ans[i * j] = j & 1 ? dec(ans[i * j], mul(ans[i], Inv[j])) : add(ans[i * j], mul(ans[i], Inv[j]));
}
for (int i = 1; i <= m; ++ i) printf("%d\n", sum = add(sum, ans[i]));
}