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);
}

浙公网安备 33010602011771号