Codeforces1555 E. Boring Segments(尺取+线段树)

传送门


题目大意

给定 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 就代表已经完全覆盖。

整理一下常见的尺取题目:

  1. 在原序列的基础上,找到一个区间,使得这个区间满足一个性质,且这个性质在维护的过程是单调的。
  2. 找到若干个数,使得这些数满足一个性质,且他们的最大值最小值之差最小(这种题目要排序后体现单调性)。
#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;
}

猜你喜欢

转载自blog.csdn.net/qq_44691917/article/details/119352894