P14621 [2019 KAIST RUN Fall] Parklife
Fonder_Wish · · 题解
春节十二响。
首先按照区间之间的包含关系建一棵树。
其实是森林,但你可以手动添加一个权值为
那么问题转化为,在树上选若干个点,每个点到根的链上最多有
:::info[怎么建树?]
将区间按照
正确性显然。 :::
从 DP 凸优化的角度,和贪心的角度是本质相同的,这里说一下前者。
考虑设计一个暴力 DP,
转移讨论是否选择
-
如果选择,
f_{u, k} \gets a_u + \sum_{v \in \operatorname{son}(u)} f_{v, k-1} 。 -
如果不选择,
f_{u, k} \gets \sum_{v \in \operatorname{son}(u)} f_{v, k} 。
容易发现固定
这个凸性自然也对应了贪心做法,和似乎也可以建出来一个费用流的模型。
那么考虑维护斜率,也就是差分数组
「先将所有孩子直接相加」就是把上凸壳每一个对应位置高度相加,当然也就是斜率(差分数组
「再做一个
考虑用堆维护
注意到这个做法和直接贪是一样的。
时间复杂度是堆启发式合并的
:::info[代码]
#include <bits/stdc++.h>
#define F(i, a, b) for (int i = (a); i <= (b); ++i)
#define dF(i, a, b) for (int i = (a); i >= (b); --i)
using namespace std;
using i64 = long long;
using i128 = __int128;
using u32 = unsigned int;
using u64 = unsigned long long;
using arr = array<int, 2>;
using vec = vector<int>;
using pii = pair<int, int>;
const int N = 2.5e5 + 5;
const int K = 1e6;
int n; array<int, 3> s[N];
int st[N], tp; vec e[N];
priority_queue<i64> f[N];
void Dfs(int u) {
for (int v : e[u]) {
Dfs(v);
if (f[u].size() < f[v].size()) swap(f[u], f[v]);
vector<i64> tmp;
while (!f[v].empty()) {
i64 x = f[u].top(), y = f[v].top();
tmp.push_back(x + y), f[u].pop(), f[v].pop();
}
for (i64 x : tmp) f[u].push(x);
}
f[u].push(s[u][2]);
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n, s[n + 1] = {1, K, 0};
F(i, 1, n) cin >> s[i][0] >> s[i][1] >> s[i][2];
auto cmp = [&](array<int, 3> x, array<int, 3> y)
{ return x[0] == y[0] ? x[1] > y[1] : x[0] < y[0]; } ;
sort(s + 1, s + n + 2, cmp);
st[tp = 1] = 1;
F(i, 2, n + 1) {
while (tp && s[st[tp]][1] < s[i][1]) --tp;
e[st[tp]].push_back(i), st[++tp] = i;
}
Dfs(1);
i64 ans = 0;
F(i, 1, n) {
if (!f[1].empty()) ans += f[1].top(), f[1].pop();
cout << ans << " ";
}
return 0;
}
:::