CF2138C2 - Maple and Tree Beauty (Hard Version)

C2. Maple and Tree Beauty (Hard Version)

This is the hard version of the problem. The difference between the versions is that in this version, the constraints on \(t\) and \(n\) are larger. You can hack only if you solved all versions of this problem.

Maple is given a rooted tree consisting of \(n\) vertices numbered from \(1\) to \(n\), where the root has index \(1\). Each vertex of the tree is labeled either zero or one. Unfortunately, Maple forgot how the vertices are labeled and only remembers that there are exactly \(k\) zeros and \(n - k\) ones.

For each vertex, we define the name of the vertex as the binary string formed by concatenating the labels of the vertices from the root to the vertex. More formally, \(\text{name}_1 = \text{label}_1\) and \(\text{name}_u = \text{name}_{p_u} + \text{label}_u\) for all \(2\le u\le n\), where \(p_u\) is the parent of vertex \(u\) and \(+\) represents string concatenation.

The beauty of the tree is equal to the length of the longest common subsequence\(^{\text{∗}}\) of the names of all the leaves\(^{\text{†}}\). Your task is to determine the maximum beauty among all labelings of the tree with exactly \(k\) zeros and \(n - k\) ones.

\(^{\text{∗}}\)A sequence \(a\) is a subsequence of a sequence \(b\) if \(a\) can be obtained from \(b\) by the deletion of several (possibly, zero or all) element from arbitrary positions. The longest common subsequence of strings \(s_1, s_2, \ldots s_m\) is the longest string that is a subsequence of all of \(s_1, s_2, \ldots, s_m\).

\(^{\text{†}}\)A leaf is any vertex without children.


引用原题解:

我们首先可以发现,最大可能的答案是所有叶子 \(d=\min{dep_u}\) 中的最小深度,因为 LCS 不可能大于所有名字的最小长度。

我们先考虑一种特殊情况,即所有叶子的深度相同。如果我们想获得最大答案 \(d\) ,那么所有叶子的名称应该完全相同。也就是说,深度相同的两个顶点的标签必须相同。

对于每个深度为 \(i\) 的顶点,我们将深度为 \(i\) 的所有顶点分组为 \(i\) (假设根的深度为 \(1\) )。对于每个组,组中的所有顶点都应该有相同的标签。假设 \(1,\cdots,d\) 组的大小为 \(c_1,\cdots,c_d\) 。现在,我们可以把这个问题看作一个 "knapsack "问题,在这个问题中,你有 \(d\) 件物品,权重为 \(c_1,\cdots,c_d\) 。我们需要找出能否从 \(c\) 中取出几个和等于 \(k\) 的数。这可以在 \(O(n^2)\) 中求解。

如果无法得到最大答案 \(d\) ,我们可以证明总是可以得到 \(w-1\) 。对于从第一组到 \(d\) 的每一组,我们都会给组中的所有顶点加上一个任意的标签,这个标签仍然至少有 \(c_i\) 个数字。由于所有叶子的深度相同, \(c_d\) 应该是数组中最大的元素,因此你总是可以为 \(1\)\(d-1\) 组选择一个标签。然后将剩余的标签任意分配给最后几组中的顶点(即叶子)。

现在,我们来考虑一般情况,即并非所有叶子的深度都相同。如果要得到答案 \(=d\) ,我们需要选择 \(d\) 个组。对于每个叶子顶点 \(v\)\(d\) 组中应该存在一个顶点,它是 \(v\) 的祖先。而对于每一个 \(2\le i\le d\) ,第 \(i\) 组中的每一个顶点 \(v\) ,第 \(i-1\) 组中都应该存在一个顶点,它是 \(v\) 的祖先。同一组中的所有顶点都应该有相同的标签。假设 \(i\) /th组的标签是 \(l_i\) ,那么我们可以得到长度为 \(d\)\(\text{LCS}=l_1l_2\ldots l_d\)

我们仍然可以将深度为 \(1\le i\le d\) 的所有顶点归为第 \(i\) 组。对于深度 \(>d\) 的所有顶点,我们可以任意设置它们的标签。我们可以把它们看作是权重为 \(1\) 的项目。

我们可以证明这是选择 \(d\) 组的最佳方法。如果深度为 \(\le d\) 的顶点 \(u\) 没有被选中,那么在 \(u\) 的子树中的某个组中一定有顶点被选中,因为从根节点到 \(u\) 的路径由小于 \(d\) 的顶点组成。如果 \(u\) 子树中的某个顶点所属的最小分组索引是 \(i\) ,我们可以让 \(u\) 位于 \(i\) -th分组内,然后可以任意选择子树 \(u\) 中所有之前位于 \(i\) 分组内的顶点的标签。如果我们考虑 knapsack 问题,就会发现 \(x\) 减少了一些 \(c_i\) ,并增加了权重为 \(1\)\(x\) 项。所有可实现的值仍然是可实现的,而一些以前无法实现的值变成了可实现的。

时间复杂度为 \(O(n^2)\)


至于 C2 的题解,给出的是 \(O(\frac{n\sqrt{n}}{w})\) 的背包优化,都上背包了,我们肯定可以考虑多项式优化。

题目要求求

\[\prod_{i = 1}^{n}\left(x^{w_i} + 1\right) \]

我们考虑 \(\ln\) 整体转化为求和形式,根据 \(\ln(x + 1)\) 的麦克劳林级数,我们知道

\[\ln(x + 1) = x - \frac{x^2}{2} + \frac{x^3}{3} - \cdots \]

于是,我们可以在 \(O(n\log{n})\) 的时间复杂度内,求出 \(\ln\) 式子的求和,然后使用多项式 \(\exp\) 求得背包的覆盖。

然后只需要检查 \(k\) 处是否被覆盖即可,时间复杂度 \(O(n\log{n})\)

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ld long double
#define ull unsigned long long
#define PII pair<ll, ll>
#define pb push_back
#define clr(f, n) memset(f, 0, sizeof(int) * (n))
#define cpy(f, g, n) memcpy(f, g, sizeof(int) * (n))
#define rev(f, n) reverse(f, f + (n))
const int N = 4e5 + 10, _G = 3, mod = 998244353, INF = 1e9;

ll qpow(ll a, ll k = mod - 2) {
    (k += mod - 1) %= mod - 1;
    ll res = 1;
    while (k) {
        if (k & 1) res = res * a % mod;
        k >>= 1;
        a = a * a % mod;
    }
    return res;
}

const int invG = qpow(_G);
int rev[N << 1], rev_len;

void rev_init(int n) {
    if (rev_len == n) return;
    for (int i = 0; i < n; i ++ ) rev[i] = (rev[i >> 1] >> 1) | (i & 1 ? n >> 1 : 0);
    rev_len = n;
}

void NTT(int *g, int op, int n) {
    rev_init(n);
    static ull f[N << 1], Gk[N << 1] = {1};
    for (int i = 0; i < n; i ++ ) f[i] = g[rev[i]];
    for (int k = 1; k < n; k <<= 1) {
        int G1 = qpow(~op ? _G : invG, (mod - 1) / (k << 1));
        for (int i = 1; i < k; i ++ ) Gk[i] = Gk[i - 1] * G1 % mod;
        for (int i = 0; i < n; i += k << 1) {
            for (int j = 0; j < k; j ++ ) {
                int tmp = Gk[j] * f[i | j | k] % mod;
                f[i | j | k] = f[i | j] + mod - tmp;
                f[i | j] += tmp;
            }
        }
        if (k == (1 << 10)) for (int i = 0; i < n; i ++ ) f[i] %= mod;
    }
    if (~op) for (int i = 0; i < n; i ++ ) g[i] = f[i] % mod;
    else {
        int invn = qpow(n);
        for (int i = 0; i < n; i ++ ) g[i] = f[i] % mod * invn % mod;
    }
}

void px(int *f, int *g, int n) {
    for (int i = 0; i < n; i ++ ) f[i] = 1ll * f[i] * g[i] % mod;
}

void covolution(int *f, int *g, int len, int lim) {
    static int sav[N << 1];
    int n; for (n = 1; n < len << 1; n <<= 1);
    clr(sav, n); cpy(sav, g, n);
    NTT(sav, 1, n); NTT(f, 1, n);
    px(f, sav, n); NTT(f, -1, n);
    clr(f + lim, n - lim), clr(sav, n);
}

int inv[N << 1], inv_len;

void inv_init(int n) {
    if (n <= inv_len) return;
    if (!inv_len) inv[0] = inv[1] = 1, inv_len = 1;
    for (int i = inv_len + 1; i <= n; i ++ ) inv[i] = 1ll * inv[mod % i] * (mod - mod / i) % mod;
    inv_len = n;
}

void Poly_d(int *f, int n) {
    for (int i = 1; i < n; i ++ ) f[i - 1] = 1ll * f[i] * i % mod;
    f[n - 1] = 0;
}

void Poly_int(int *f, int n) {
    for (int i = n; i; i -- ) f[i] = 1ll * f[i - 1] * inv[i] % mod;
    f[0] = 0;
}

void Poly_inv(int *f, int m) {
    static int g1[N << 1], g2[N << 1], sav[N << 1];
    int n; for (n = 1; n < m; n <<= 1);
    g1[0] = qpow(f[0]);
    for (int len = 2; len <= n; len <<= 1) {
        cpy(g2, g1, len >> 1), cpy(sav, f, len);
        NTT(g2, 1, len), NTT(sav, 1, len);
        px(g2, sav, len); NTT(g2, -1, len);
        clr(g2, len >> 1); cpy(sav, g1, len);
        NTT(sav, 1, len); NTT(g2, 1, len);
        px(g2, sav, len); NTT(g2, -1, len);
        for (int i = len >> 1; i < len; i ++ ) g1[i] = (2ll * g1[i] - g2[i] + mod) % mod;
    }
    cpy(f, g1, m), clr(g1, n), clr(g2, n), clr(sav, n);
}

void Poly_ln(int *f, int n) {
    static int sav[N << 1];
    inv_init(n); cpy(sav, f, n);
    Poly_d(sav, n); Poly_inv(f, n);
    covolution(f, sav, n, n); Poly_int(f, n - 1);
    clr(sav, n);
}

void Poly_exp(int *f, int m) {
    static int b1[N << 1], b2[N << 1];
    int n; for (n = 1; n < m; n <<= 1);
    b1[0] = 1;
    for (int len = 2; len <= n; len <<= 1) {
        cpy(b2, b1, len >> 1); Poly_ln(b2, len);
        for (int i = 0; i < len; i ++ ) b2[i] = (f[i] - b2[i] + mod) % mod;
        b2[0] = (b2[0] + 1) % mod;
        covolution(b1, b2, len, len);
    }
    cpy(f, b1, m); clr(b1, n); clr(b2, n);
}

ll n, k, maxn;
int F[N << 1], G[N << 1];

void solve() {
    cin >> n >> k;
    vector<int> dep(n + 1), p(n + 1), node(n + 1), a(n + 1), cnt(n + 1);
    dep[1] = 1;
    for (int i = 2; i <= n; i ++ ) {
        cin >> p[i];
        dep[i] = dep[p[i]] + 1;
        node[p[i]] = 1;
    }
    int min_dep = INF;
    for (int i = 1; i <= n; i ++ ) {
        if (!node[i]) min_dep = min(min_dep, dep[i]);
    }
    for (int i = 1; i <= n; i ++ ) {
        if (dep[i] <= min_dep) a[dep[i]] ++ ;
        else cnt[1] ++ ;
    }
    for (int i = 1; i <= n; i ++ ) {
        if (a[i]) cnt[a[i]] ++ ;
    }
    k = min(k, n - k);
    inv_init(k + 1);
    for (int i = 1; i <= k; i ++ ) {
        for (int j = 1; i * j <= k; j ++ ) {
            if (j & 1) F[i * j] = (F[i * j] + 1ll * inv[j] * cnt[i] % mod) % mod;
            else F[i * j] = (F[i * j] - 1ll * inv[j] * cnt[i] % mod + mod) % mod;
        }
    }
    Poly_exp(F, n + 1);
    if (F[k]) cout << min_dep << "\n";
    else cout << min_dep - 1 << "\n";
    clr(F, n + 1);
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    int T = 1;
    cin >> T;
    while (T -- ) solve();
    return 0;
}
posted @ 2025-09-09 11:13  YipChip  阅读(56)  评论(0)    收藏  举报