方差
现在有 \(n\) 个区间 \([l_i,r_i]\),每个区间有个权值 \(w_i\)。 我们把这 \(n\) 个区间当成 \(n\) 个 点,如果两个区间它们之间有交(包括端点),那么我们就在这两个区间之间连边,形成了一个区间图。
现在希望你删除一些区间,使得每个连通块大小不超过 \(k\)。
输出删除区间最小的权值和。
显然不能考虑建图,o(╥﹏╥)o,因为我就直接建了图。
这是一道弱智 DP 题,想到 DP 后就比较容易了。
先离散,设 \(f_i\) 表示 \([1,i]\) 中最后一次分割在 \(i\) 的答案。
具体思路是:相当于将所有区间割成若干个单独的块,再在每个块中选最大的 \(k\) 个保留。
\[f_i=\max\{f_{j-1}+sum\}\quad\text{(sum 表示最大的 $k$ 个)}
\]
最大的 \(k\) 个用优先队列维护即可。
namespace SilverWolf {
struct node { int l,r; ll w; };
int n,k; // 每个联通块大小为k
node a[maxn + 5];
ll f[maxn + 5]; // i为最后的分割位置(离散后)
ll x[2 * maxn + 5],xcnt = 0;
map<ll,int> mp;
vector<pair<int,int> > book[2 * maxn + 5];
vector<int> bookl[2 * maxn + 5];
void init() { // 离散化
sort(x + 1,x + xcnt + 1);
int mpcnt = 0;
for (int i = 1;i <= xcnt;i ++) if (!mp.count(x[i])) mp[x[i]] = ++ mpcnt;
for (int i = 1;i <= n;i ++) {
a[i].l = mp[a[i].l],a[i].r = mp[a[i].r];
book[a[i].r].push_back(make_pair(a[i].l,i));
}
}
ll sum = 0ll;
void main() {
cin >> n >> k;
for (int i = 1;i <= n;i ++) {
cin >> a[i].l >> a[i].r >> a[i].w; sum += a[i].w;
x[++ xcnt] = a[i].l,x[++ xcnt] = a[i].r;
}
init();
priority_queue<ll,vector<ll>,greater<ll> > q;
for (int i = 1;i <= 2 * n;i ++) {
ll sum = 0ll; // 维护前k大
while (!q.empty()) q.pop();
for (pair<int,int> p : book[i]) bookl[p.first].push_back(p.second);
for (int j = i;j >= 1;j --) {
for (int v : bookl[j]) {
if ((int)q.size() < k) q.push(a[v].w),sum += a[v].w;
else if (a[v].w > q.top()) {
sum -= q.top(); q.pop();
q.push(a[v].w),sum += a[v].w;
}
}
f[i] = max(f[i],sum + f[j - 1]);
}
}
cout << sum - f[2 * n];
}
}

浙公网安备 33010602011771号