题目大意
给定 n n n 区间,每个区间都有一个权值。给定一个数 m m m,选择若干个区间能覆盖区间 [ 1 , m ] [1,m] [1,m] 且一定是连通的区间集(这个连通指的是存在每个区间都存在另外一个区间和它有交集),且最大权值和最小权值相差最小。输出最大最小值的差值。
解题思路
首先考虑如何使最大最小值的差值最小,因为区间什么顺序都无所谓,所以干脆按照权值从小到大排序。考虑当前的区间 i i i,若能在它的右边尽可能靠左的位置确定一个两两有交集且覆盖 [ 1 , m ] [1,m] [1,m] 的区间集合,那么就不会考虑更靠右的位置了。这实际上是一种单调性,也就是说如果选择了更多的区间满足了条件,那么对于区间 i i i,它的右边界一定会更靠右而不会靠左。这实际上就是双指针(尺取)的典型应用场景。
如何维护区间是否覆盖 [ 1 , m ] [1,m] [1,m] 且连通?实际上如何覆盖还是很简单的,连通比较难。于是可以转化一下,把每个区间的 r − − r-- r−−,而且将 m − − m-- m−−,这样就转化成了覆盖区间 [ 1 , m ] [1,m] [1,m]。使用线段树,每次覆盖一个区间就做区间加法,维护区间最小值, [ 1 , m ] [1,m] [1,m] 的最小值不为 0 就代表已经完全覆盖。
整理一下常见的尺取题目:
- 在原序列的基础上,找到一个区间,使得这个区间满足一个性质,且这个性质在维护的过程是单调的。
- 找到若干个数,使得这些数满足一个性质,且他们的最大值最小值之差最小(这种题目要排序后体现单调性)。
#include <bits/stdc++.h>
using namespace std;
#define lowbit(x) (x & (-x))
#define ENDL "\n"
typedef long long ll;
typedef unsigned long long ull;
typedef pair<double, double> pii;
const double eps = 1e-4;
const int Mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const int maxn = 2e6 + 10;
int tree[maxn << 2], lz[maxn << 2];
void pushdown(int i, int l, int r) {
if (lz[i]) {
int mid = (l + r) >> 1, k = i << 1;
tree[k] += lz[i], tree[k | 1] += lz[i];
lz[k] += lz[i], lz[k | 1] += lz[i], lz[i] = 0;
}
}
void pushup(int i) {
tree[i] = min(tree[i << 1], tree[i << 1 | 1]); }
void add(int i, int l, int r, int x, int y, int val) {
if (x <= l && r <= y) {
tree[i] += val;
lz[i] += val;
return;
}
pushdown(i, l, r);
int mid = (l + r) >> 1, k = i << 1;
if (x <= mid) add(k, l, mid, x, y, val);
if (y > mid) add(k | 1, mid + 1, r, x, y, val);
pushup(i);
}
struct node {
int l, r, w;
} a[maxn / 5];
bool cmp(node &p, node &q) {
return p.w < q.w; }
int main() {
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n, m;
cin >> n >> m;
m--;
for (int i = 1; i <= n; i++) {
cin >> a[i].l >> a[i].r >> a[i].w;
a[i].r--;
}
sort(a + 1, a + 1 + n, cmp);
int l = 1, r = 1, ans = inf;
while (l <= n) {
while (r <= n && !tree[1]) {
add(1, 1, m, a[r].l, a[r].r, 1);
r++;
}
if (!tree[1]) break;
ans = min(ans, a[r - 1].w - a[l].w);
add(1, 1, m, a[l].l, a[l].r, -1);
l++;
}
cout << ans << endl;
return 0;
}