CF 1555 E. Boring Segments

E. Boring Segments

题目大意

给定一个数轴,\(1\to m\),以及\(m\)个线段的\(l_i, r_i, w_i\),为线段的左端点,右端点,以及价值。

现在要求选择一些线段,使得能够从数轴上的\(1\)出发,沿着线段走,能够到达\(m\)

代价是选择的线段的价值的极差。

问最小的代价值。

解题思路

我们将线段按照价值从小到大排序,然后依次枚举价值最小的线段。

然后不断地往右取线段,直到能够从\(1\)走到\(m\),此时即为一个答案。

随着价值最小的线段不断右移,价值最大的线段也在不断右移,所谓的双指针法。

此时只要考虑如何维护选择的线段覆盖的信息。

由于\(m\leq 10^6\),维护每个数字被线段覆盖的次数,用线段树维护即可,区间加法和区间最小值。

注意到仍存在一点问题,即如果一条线段覆盖\(1,2,3\),另一条线段覆盖\(4,5\),此时从\(1\)不能达到\(5\),我们还需要一条线段覆盖\(3,4\)也就是说3,4之间再插入一个点

因此将所有坐标都乘以\(2\),为每个数字之间都插入一个点,然后再区间加法和区间查询最小值即可。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

const int N = 2e6 + 8;

class Segment{
#define lson root << 1
#define rson root << 1 | 1

    int minn[N << 2];
    int lazy[N << 2];

    public:

    void pushdown(int root){
        if (lazy[root] == 0)
            return;
        minn[lson] += lazy[root];
        minn[rson] += lazy[root];
        lazy[lson] += lazy[root];
        lazy[rson] += lazy[root];
        lazy[root] = 0;
    }

    void update(int root, int l, int r, int ll, int rr, int val){
        if (ll <= l && r <= rr){
            minn[root] += val;
            lazy[root] += val;
            return;
        }
        pushdown(root);
        int mid = (l + r) >> 1;
        if (ll <= mid)
            update(lson, l, mid, ll, rr, val);
        if (rr > mid)
            update(rson, mid + 1, r, ll, rr, val);
        minn[root] = min(minn[lson], minn[rson]);
    }

    inline bool query(){
        return minn[1] != 0;
    }
}Seg;

int n, m;

pair<pair<int,int>, int> s[N];

int id[N];

int main(void) {
    ios::sync_with_stdio(false); 
    cin.tie(0); cout.tie(0);
    cin >> n >> m;
    m <<= 1;
    for(int i = 1; i <= n; ++ i){
        cin >> s[i].first.first >> s[i].first.second >> s[i].second;
        s[i].first.first <<= 1;
        s[i].first.second <<= 1;
        id[i] = i;
    }
    sort(id + 1, id + 1 + n, [&](int x, int y){
            return s[x].second < s[y].second;
            });
    int cur = 1;
    int ans = 1e9 + 7;
    for(int i = 1; i <= n; ++ i){
        while(cur <= n && !Seg.query()){
            Seg.update(1, 2, m, s[id[cur]].first.first, s[id[cur]].first.second, 1);
            ++ cur;
        }
        if (Seg.query())
            ans = min(ans, s[id[cur - 1]].second - s[id[i]].second);
        Seg.update(1, 2, m, s[id[i]].first.first, s[id[i]].first.second, -1);
    }
    cout << ans << '\n';
    return 0;
}


posted @ 2021-09-08 10:52  ~Lanly~  阅读(52)  评论(0编辑  收藏  举报