题解 CF1555E Boring Segments
tips
智商不够,码量来凑。
在给定的线段中选出一个线段权值极差最小的子集,使得其覆盖了 \([1, m]\)。
比赛结束前最后 \(15\) 分钟想出了一个较为复杂的解法,不仅码量大,而且跑得还慢,所以没能在赛场上切了这题。
看到题目首先有的一个想法是二分,在考虑验证的时候发现按照权值排好序后区间两个指针的移动都是单调的,那么就直接双指针。
但还需要一个数据结构来维护快速覆盖以及查询一段是否被完全覆盖。
赛场上想到的是线段树存两个关键信息,一个是这段区间内有几个被覆盖不为 \(0\),另一个是覆盖的和,可是这样子 我也不确定是否可做 两个信息以及 tag
的维护还是挺多细节的,最后还得线段树上二分,15 分钟肯定写不完。
其实完全做复杂了。
首先,都双指针了还二分什么,二分出来的那个限制不要也罢,反正移动到覆盖再停。
然后维护线段覆盖的做法也太复杂了,实际上只要维护区间被覆盖次数的最小值就行了,这样只需要一个更新操作,查询的时候直接用 t[1].min
就可以了。
所以下次遇到看上去很复杂的题目时要多动动脑子,极有可能有简单得多的做法。
代码
#include <iostream>
#include <algorithm>
const int N = 300005, M = 1000005;
int n, m, ans = 0x3f3f3f3f;
struct twt {
int l, r, w;
bool operator < (twt b) const { return w < b.w; }
} a[N];
struct dwd { int min, tag; } t[8*M];
void pushup(int p) { t[p].min = std::min(t[p+p].min, t[p+p+1].min); }
void pushdown(int p) {
t[p+p].min += t[p].tag, t[p+p+1].min += t[p].tag;
t[p+p].tag += t[p].tag, t[p+p+1].tag += t[p].tag;
t[p].tag = 0;
}
void add(int p, int l, int r, int x, int y, int z) {
if (l == x && y == r) return t[p].tag += z, t[p].min += z, void();
pushdown(p);
int mid = l + (r-l) / 2;
if (y <= mid) add(p+p, l, mid, x, y, z);
else if (x > mid) add(p+p+1, mid+1, r, x, y, z);
else add(p+p, l, mid, x, mid, z), add(p+p+1, mid+1, r, mid+1, y, z);
pushup(p);
}
int main() {
std::cin >> n >> m;
for (int i = 1; i <= n; i++) std::cin >> a[i].l >> a[i].r >> a[i].w, a[i].r --;
std::sort(a+1, a+1+n);
for (int i = 1, j = 0; i <= n; i++) {
while (j+1 <= n && t[1].min < 1) j++, add(1, 1, m-1, a[j].l, a[j].r, 1);
if (t[1].min >= 1) ans = std::min(ans, a[j].w - a[i].w);
add(1, 1, m-1, a[i].l, a[i].r, -1);
}
std::cout << ans;
}