P5979 [PA 2014] Druzyny

先考虑一个 \(O(n^2)\) dp:

\(f_i = \max \limits_{j < i \land G(j + 1, i)} f_j + 1\),其中 \(G(a, b) =\) 连续段 \([a, b]\) 满足约束条件。

发现合法的 \(j\) 不连续,尝试使用 cdq 分治优化,考虑如何计算 \(f_{[l, mid]}\)\(f_{[mid + 1, r]}\) 的贡献。

\(i \in [l, mid], j \in [mid + 1, r]\),发现不好快速处理 \(G(i, j)\) ,尝试拆分为 \([i, mid]\)\([mid + 1, j]\):对于每个 \(i\),确定它“能贡献”的 \(j\) 的范围(基于 \([i, mid]\) 的条件);对于每个 \(j\),确定它能“接受”的 \(i\) 的范围(基于 \([mid + 1, j]\) 的条件)。

“能贡献”和“接受”的部分都是连续段,可以使用扫描线处理,总时间复杂度 \(O(n\log^2n)\)

Code:

void solve(int l, int r) {
    if (l == r) return;
    solve(l, mid);
    int x = 1, y = n;
    for (int i = mid; i >= l && x <= y; i--) {
        int L = std::max(mid + 1, i + x);
        int R = std::min(r, i + y) + 1;
        if (L < R) {
            vc[L].emplace_back(i, f[i]);
            vc[R].emplace_back(i, Node()); // 清空
        }
        x = std::max(x, c[i]), y = std::min(y, d[i]);
    }
    x = 1, y = n;
    for (int i = mid + 1; i <= r + 1; i++) {
        for (auto [id, v] : vc[i]) upd(1, 0, n, id, v);
        vc[i].clear();
        if (i == r + 1) break;
        x = std::max(x, c[i]), y = std::min(y, d[i]);
        if (x > y) continue;
        Node res = qry(1, 0, n, i - y, i - x);
        res.mx++;
        f[i] = f[i] + res; // 重载运算符,此处为取 max
    }
    solve(mid + 1, r);
}
posted @ 2026-01-07 21:36  zjh114514  阅读(1)  评论(0)    收藏  举报