[BZOJ4653][UOJ222][Noi2016]区间(离散化+尺取法+线段树)

看到区间端点是 109 10 9 级别的,果断离散化。
然后把所有区间按照离散化之前的长度从小到大排序。
考虑如果在排序后连续的一段中选出 M M 个区间,那么如何判断是否存在一个合法方案呢?
线段树!
即维护一个序列,一开始全部为0
然后对每个区间进行这样一个处理:对每个区间 [l,r] [ l , r ] ,把序列的 [l,r] [ l , r ] 范围内的所有数都加上 1 1
处理完毕后,如果整个序列的最大值M,那么有解。
又由于要求最小代价,因此考虑尺取法:
维护两个指针 L,R L , R ,表示当前方案下选取的区间是从第 L L 个区间到第R个区间。并且这个选取方案是合法的。
每次把右指针 R R 右移一个单位之后,L也要相应地右移,具体是:
如果把第 L L 个区间去掉就不存在合法方案(把所有区间加入都无法让序列的最大值M), L L 就不再右移,否则不断右移,直到不能再右移为止。
期间用线段树维护序列,并且不断地用第R个区间的长度 L个区间的长度更新答案。
代码:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define p2 p << 1
#define p3 p << 1 | 1
using namespace std;
inline int read()
{
    int res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    return bo ? ~res + 1 : res;
}
typedef long long ll;
const int N = 5e5 + 5, M = N << 1, L = N << 3;
struct cyx
{
    int l, r, xl, xr, len;
} a[N];
ll ans = 1ll << 62;
inline bool comp(const cyx &a, const cyx &b)
{
    return a.len < b.len;
}
int n, m, tm, orz[M], T[L], add[L];
void down(int p)
{
    add[p2] += add[p];
    add[p3] += add[p];
    add[p] = 0;
}
void upt(int p)
{
    T[p] = max(T[p2] + add[p2], T[p3] + add[p3]);
}
void change(int l, int r, int s, int e, int v, int p)
{
    if (l == s && r == e) return (void) (add[p] += v);
    int mid = l + r >> 1;
    down(p);
    if (e <= mid) change(l, mid, s, e, v, p2);
    else if (s >= mid + 1) change(mid + 1, r, s, e, v, p3);
    else change(l, mid, s, mid, v, p2),
        change(mid + 1, r, mid + 1, e, v, p3);
    upt(p);
}
int main()
{
    n = read(); m = read();
    for (int i = 1; i <= n; i++)
    {
        a[i].xl = read(); a[i].xr = read();
        a[i].len = a[i].xr - a[i].xl;
        orz[(i << 1) - 1] = a[i].xl, orz[i << 1] = a[i].xr;
    }
    sort(orz + 1, orz + (n << 1) + 1);
    tm = unique(orz + 1, orz + (n << 1) + 1) - orz - 1;
    for (int i = 1; i <= n; i++)
    {
        a[i].l = lower_bound(orz + 1, orz + tm + 1, a[i].xl) - orz;
        a[i].r = lower_bound(orz + 1, orz + tm + 1, a[i].xr) - orz;
    }
    sort(a + 1, a + n + 1, comp);
    for (int r = 1, l = 1; r <= n; r++)
    {
        change(1, n << 1, a[r].l, a[r].r, 1, 1);
        while (l < r && T[1] + add[1] >= m)
            change(1, n << 1, a[l].l, a[l].r, -1, 1), l++;
        if (l > 1 && T[1] + add[1] < m)
            change(1, n << 1, a[l - 1].l, a[l - 1].r, 1, 1), l--;
        if (T[1] + add[1] >= m)
            ans = min(ans, 1ll * (a[r].len - a[l].len));
    }
    if (ans == (1ll << 62)) puts("-1");
    else cout << ans << endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值