cf Educational Codeforces Round E. Boring Segments

原题:
E. Boring Segments
time limit per test
3 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

You are given n
segments on a number line, numbered from 1 to n. The i-th segments covers all integer points from li to ri and has a value wi.

You are asked to select a subset of these segments (possibly, all of them). Once the subset is selected, it’s possible to travel between two integer points if there exists a selected segment that covers both of them. A subset is good if it’s possible to reach point m
starting from point 1 in arbitrary number of moves.

The cost of the subset is the difference between the maximum and the minimum values of segments in it. Find the minimum cost of a good subset.

In every test there exists at least one good subset.

Input
The first line contains two integers n
and m (1≤n≤3⋅10^5; 2≤m≤10^6) — the number of segments and the number of integer points.

Each of the next n lines contains three integers li, ri and wi (1≤li<ri≤m; 1≤wi≤10^6) — the description of the i-th segment.

In every test there exists at least one good subset.
Output

Print a single integer — the minimum cost of a good subset.
Examples
Input

5 12
1 5 5
3 4 10
4 10 6
11 12 5
10 12 3

Output

3

Input

1 10
1 10 23

Output

0

中文:
给你n个区间,每个区间的端点分别为 l i , r i l_i,r_i li,ri,区间的权重是 w i w_i wi
现在让你选其中的一些区间,使得这些区间能够覆盖[1,m],并且选定的这些区间中的最大权重减去最小权重的值最小。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e6 + 5;
 
int n,m;
struct node {
    int l;
    int r;
    int w;
};
node ns[maxn];
int val[maxn << 2];
int mark[maxn << 2];
 
 
void pushUp(int rt) {
    val[rt] = min(val[rt << 1], val[rt << 1|1]);
}
 
void pushDown(int rt) {
    if (mark[rt]) {
        mark[rt << 1] += mark[rt];
        mark[rt << 1|1] += mark[rt];
        val[rt << 1] += mark[rt];
        val[rt << 1|1] += mark[rt];
        mark[rt] = 0;
    }
}
 
void update(int st, int ed, int v, int L, int R, int rt) {
    if (L >= st && R <= ed) {
        mark[rt] += v;
        val[rt] += v;
        return;
    }
    pushDown(rt);
    int M = (L+R) >> 1;
    if (st <= M) {
        update(st, ed, v, L, M, rt << 1);
    }
    if (ed > M) {
        update(st, ed, v, M + 1, R, rt << 1 | 1);
    }
    pushUp(rt);
}
 
bool cmp(const node& lhs, const node& rhs) {
    return lhs.w < rhs.w;
}
 
int main()
{
    ios::sync_with_stdio(false);
    while (cin >> n >> m) {
        memset(mark,0 ,sizeof(mark));
        memset(val, 0 ,sizeof(val));
        int l,r,w;
        m--;
        for (int i=1;i<=n;i++) {
            cin>> ns[i].l >> ns[i].r >> ns[i].w;
            ns[i].r--;
        }
        sort(ns + 1, ns + 1 +n, cmp);
        int ans = INT_MAX;
 
        for (int rp = 1, lp = 1; rp<=n; rp++) {
            update(ns[rp].l, ns[rp].r, 1, 1, m, 1);
            while(val[1] > 0) {
                ans = min(ans, ns[rp].w - ns[lp].w);
                update(ns[lp].l, ns[lp].r, -1, 1, m, 1);
                lp++;
            }
        }
        cout << ans << endl;
    }
    return 0;
}

解答:

这题首先会想到对线段安装权重排序,假设一共有n个线段,假设取了排序后第i个到第j个线段。
那么,权重的最大值减去最小值明显就是 w j − w i w_j-w_i wjwi,那这第i个到第j个线段能否覆盖[1,m]吗?
区间上动态判断线段覆盖问题问题肯定是用线段树了,对选定的i到j个线段所覆盖的区域的每个点用加1的方式计数,最后判断[1,m]中所有点计数的最小值是否是大于等于1的,如果满足,则可以断定i到j个线段是可以覆盖全部[1,m];如果[1,m]所有点计数的最小值是0,说明存在一些点没有被选定的i到j个线段覆盖到。

接下来考虑如何枚举区间?
从给定的数据量来看,n^2的暴力是不行的。
这里使用尺取的方法来选定线段的集合,假设两个下标i和j,表示当前已经选定了第i个下标到第j个下标之间的线段。并且,此时通过线段树判断,满足将[1,m]覆盖的要求。根据尺取法的策略,将下标i进行递增,递增的同时判断新的i到j之间的线段是否可以覆盖[1,m],同时更新最大的权重减去最小的权重的值。
如果选定的i到j个线段没有办法覆盖[1,m],那么j递增,直到满足i到j个线段可以覆盖[1,m],保留此时的权重差值即可。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值