方差

现在有 \(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];
	}
}

posted @ 2026-08-30 09:56  Denia-kawaii  阅读(3)  评论(0)    收藏  举报