[NOI2016]区间 题解

[NOI2016]区间 题解

题目地址:洛谷:【P1712】[NOI2016]区间 – 洛谷、BZOJ:Problem 4653. — [Noi2016]区间

题目描述

在数轴上有 n个闭区间 [l1,r1],[l2,r2],…,[ln,rn]。现在要从中选出 m 个区间,使得这 m个区间共同包含至少一个位置。换句话说,就是使得存在一个 x,使得对于每一个被选中的区间 [li,ri],都有 li≤x≤ri。
对于一个合法的选取方案,它的花费为被选中的最长区间长度减去被选中的最短区间长度。区间 [li,ri] 的长度定义为 ri−li,即等于它的右端点的值减去左端点的值。
求所有合法方案中最小的花费。如果不存在合法的方案,输出 −1。

题意简述

给你$n$个区间,求选取这些区间的一个子集,使得这个子集中的区间交集不为空,一种选区方案的花费定义为最长区间长度减去最短区间长度,求花费最小的选取方案。

输入输出格式

输入格式:
第一行包含两个正整数 n,m用空格隔开,意义如上文所述。保证 1≤m≤n
接下来 n行,每行表示一个区间,包含用空格隔开的两个整数 li 和 ri 为该区间的左右端点。
N<=500000,M<=200000,0≤li≤ri≤10^9

输出格式:
只有一行,包含一个正整数,即最小花费。

输入输出样例

输入样例#1:

6 3
3 5
1 2
3 4
2 2
1 5
1 4

输出样例#1:

2

题解

我们对区间按长度排序,贪心地选择尽量短的长度单调的一段,使得这一段中存在一个点被覆盖了至少$m$次,则我们可以用这一段的最长长度减最短长度更新答案。可以证明,这样做一定能找到最优解,相当于在枚举每一个最短长度,贪心地找到最小的最长长度。
判断是否被覆盖$m$次,只需要把区间覆盖转换成线段树上的区间加法,维护全局最大即可。
复杂度$O(n \log n)$。由于左右端点比较大需要离散化。

代码

// Code by KSkun, 2018/7
#include <cstdio>
#include <cctype>
#include <cstring>

#include <algorithm>
#include <vector>

typedef long long LL;

inline char fgc() {
    static char buf[100000], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2)
        ? EOF : *p1++;
}

inline LL readint() {
    register LL res = 0, neg = 1; register char c = fgc();
    for(; !isdigit(c); c = fgc()) if(c == '-') neg = -1;
    for(; isdigit(c); c = fgc()) res = res * 10 + c - '0';
    return res * neg;
}

inline char readsingle() {
    register char c;
    while(!isgraph(c = fgc())) {}
    return c;
}

const int MAXN = 500005;

int n, m, N;

struct Seg {
    int l, r, len;
    inline bool operator<(const Seg &rhs) const {
        return len < rhs.len;
    }
} segs[MAXN];

#define lch o << 1
#define rch o << 1 | 1
#define mid ((l + r) >> 1)

int mx[MAXN << 3], tag[MAXN << 3];

inline void pushdown(int o) {
    if(tag[o]) {
        tag[lch] += tag[o]; tag[rch] += tag[o];
        mx[lch] += tag[o]; mx[rch] += tag[o];
        tag[o] = 0;
    }
}

void add(int o, int l, int r, int ll, int rr, int v) {
    if(l >= ll && r <= rr) {
        mx[o] += v; tag[o] += v; return;
    }
    pushdown(o);
    if(ll <= mid) add(lch, l, mid, ll, rr, v);
    if(rr > mid) add(rch, mid + 1, r, ll, rr, v);
    mx[o] = std::max(mx[lch], mx[rch]);
}

std::vector<int> tmp;

int main() {
    n = readint(); m = readint();
    tmp.push_back(-1);
    for(int i = 1; i <= n; i++) {
        segs[i].l = readint(); segs[i].r = readint();
        segs[i].len = segs[i].r - segs[i].l;
        tmp.push_back(segs[i].l);
        tmp.push_back(segs[i].r);
    }
    std::sort(tmp.begin(), tmp.end());
    tmp.erase(std::unique(tmp.begin(), tmp.end()), tmp.end());
    N = tmp.size() - 1;
    for(int i = 1; i <= n; i++) {
        segs[i].l = std::lower_bound(tmp.begin(), tmp.end(), segs[i].l) - tmp.begin();
        segs[i].r = std::lower_bound(tmp.begin(), tmp.end(), segs[i].r) - tmp.begin();
    }
    std::sort(segs + 1, segs + n + 1);
    int r = 0, ans = 2e9;
    for(int l = 1; l <= n; l++) {
        while(r < n && mx[1] < m) {
            r++; add(1, 1, N, segs[r].l, segs[r].r, 1);
        }
        if(mx[1] >= m) ans = std::min(ans, segs[r].len - segs[l].len);
        add(1, 1, N, segs[l].l, segs[l].r, -1);
    }
    printf("%d", ans == 2e9 ? -1 : ans);
    return 0;
}


发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据