经典种树
本题其实就是 P1484 种树。
因为需要进行到询问包含结点区间或者与结点区间相离,所以每一个区间询问
线段树中,如果有从未访问到的结点,那么就一定是那些在任何询问中,都处于同一个区间中的下标构成的区间。所以,为了讨论访问到的结点的最小深度,不妨将这些从不分离的连续的下标段看作是一个下标。做了这样的简化后,线段树中所有结点都会访问到,且结点的总数目就是简化后的下标数目。
要让最深的结点深度最浅,只能是尽量构造为满二叉树。
设简化后,下标数目为
访问一对叶子节点
不妨将讨论的对象设为
然后,模拟费用流、反悔贪心、WQS 二分,随便选,最终复杂度是
提供一个 WQS 二分的核心代码:
// Find the maximum of unimodel function F.
// The domain of F must be consecutive integer values.
template <std::integral T, typename F>
auto golden_section_search(T ll, T rr, F func) {
constexpr long double phi = (std::sqrt(5.0l) - 1) / 2;
T ml = ll + (rr - ll) * (1 - phi);
T mr = ll + (rr - ll) * phi;
auto fl = func(ml), fr = func(mr);
while (ml < mr) {
if (fl > fr) {
rr = mr;
mr = ml;
fr = fl;
ml = ll + (rr - ll) * (1 - phi);
fl = func(ml);
} else {
ll = ml;
ml = mr;
fl = fr;
mr = ll + (rr - ll) * phi;
fr = func(mr);
}
}
for (auto i = ll; i <= rr; ++i) {
auto fi = func(i);
if (fi > fr) {
fr = fi;
mr = i;
}
}
return std::pair(mr, fr);
};
int main() {
int n, q;
std::cin >> n >> q;
std::vector<int> a(n + 1);
for (; q; --q) {
int l, r;
std::cin >> l >> r;
++a[l - 1];
++a[r];
}
int cnt = 0;
for (int i = 1; i < n; ++i) {
cnt += a[i] > 0;
}
if (!cnt) {
std::cout << 0 << ' ' << a[0] << std::endl;
} else {
int d = 0;
for (; (1 << d) < cnt + 1; ++d);
int m = cnt + 1 - (1 << (d - 1));
std::vector c(cnt + 1, 0);
for (int i = 1, j = 0; i < n; ++i) {
if (a[i]) {
c[++j] = a[i];
}
}
n = cnt;
auto solve = [&](int k) -> long long {
long long dp[2] = { 0, 0x3f3f3f3f3f3f3f3f };
for (int i = 1; i <= n; ++i) {
std::tie(dp[0], dp[1]) = std::make_pair(
std::min(dp[0], dp[1]),
dp[0] + c[i] - k
);
}
return std::min(dp[0], dp[1]);
};
auto [k, v] = golden_section_search(0, 10000000, [&](int k) -> long long {
return solve(k) + (long long)k * m;
});
std::cout << d << ' ' << (v << 1) << std::endl;
}
return 0;
}