zoukankan      html  css  js  c++  java
  • 区间[NOI2016]

    题目描述

    在数轴上有 (n) 个闭区间从 (1)(n) 编号,第 (i) 个闭区间为 ([l_i,r_i])

    现在要从中选出 (m) 个区间,使得这 (m) 个区间共同包含至少一个位置。换句话说,就是使得存在一个 (x) ,使得对于每一个被选中的区间 ([l_i,r_i])

    对于一个合法的选取方案,它的花费为被选中的最长区间长度减去被选中的最短区间长度。区间 ([l_i,r_i]) 的长度定义为 ((r_i-l_i)) 即等于它的右端点的值减去左端点的值。

    求所有合法方案中最小的花费。如果不存在合法的方案,输出 (−1)

    输入格式

    第一行包含两个整数,分别代表 (n)(m)

    (2) 到第 ((n + 1)) 行,每行两个整数表示一个区间,第 ((i + 1)) 行的整数 (l_i, r_i) 分别代表第 (i) 个区间的左右端点。

    输出格式

    输出一行一个整数表示答案。

    题解

    将区间按长度从小到大排序,记为 (p_1,p_2,cdots p_n)

    假设最后选的区间中长度最短和最长的分别是 (p_l, p_r) ,那么答案就是 (p_r) 的长度减 (p_l) 的长度,在 (p_{l+1}sim p_{r-1}) 中选哪些并不会给答案造成影响

    判定一个答案是否合法的方法:假设现在有一个长度为1e9的序列,每个位置的值都是 (0) ,如果我们对于 (p_l sim p_r) 的每个区间,都把这个区间中的每个位置+1,那么操作完后如果有某个位置的值大于 (m) ,那么就说明这个位置被 (p_l sim p_r) 中的至少 (m) 个区间覆盖了,也就是说存在一种合法方案只选择了 (m)(p_lsim p_r) 中的区间,就可以用 ((p_r) 的长度减 (p_l) 的长度) 来更新答案

    如果选择的最短区间为 (p_l) 时,使得代价最小的方案中选择的的最长区间是 (p_r),那么选择的最短区间固定为 (p_{l+1}) 时,此时的最优方案选择的最长区间一定比 (p_r)

    证明:假设最短区间固定为 (p_{l+1}) 时,此时的最优方案选择的最长区间是 (p_k),且 (k < r),那么按照上面那个判合法性的方法,选择 (p_lsim p_k) 也一定是合法的,矛盾

    所以可以使用双指针的方法,每次固定左指针(即最短区间),然后移动右指针找到第一个满足条件的最长区间,然后更新答案

    上面的判定合法的方法需要区间加,查询全局最大值,用线段树维护即可

    为方便线段树维护,可以先给区间端点进行离散化

    代码

    #include <bits/stdc++.h>
    #define N 1000005
    using namespace std;
    
    template <typename T>
    inline void read(T &num) {
    	T x = 0, f = 1; char ch = getchar();
    	for (; ch > '9' || ch < '0'; ch = getchar()) if (ch == '-') f = -1;
    	for (; ch <= '9' && ch >= '0'; ch = getchar()) x = (x << 3) + (x << 1) + (ch ^ '0');
    	num = x * f;
    }
    
    int n, m, len[N], srt[N], mx;
    struct intv {
    	int l, r;
    } p[N];
    bool cmp(intv x, intv y) { return x.r-x.l < y.r-y.l; }
    
    void fCuCcFk() {
    	for (int i = 1; i <= n; i++) srt[++mx] = p[i].l, srt[++mx] = p[i].r;
    	sort(srt+1, srt+mx+1);
    	mx = unique(srt+1, srt+mx+1) - srt - 1;
    	for (int i = 1; i <= n; i++) {
    		len[i] = p[i].r - p[i].l + 1;
    		p[i].l = lower_bound(srt+1, srt+mx+1, p[i].l) - srt;
    		p[i].r = lower_bound(srt+1, srt+mx+1, p[i].r) - srt; 
    	} 
    }
    
    struct segtree {
    	int mx, tag;
    } tr[N<<2];
    void build(int ind, int l, int r) {
    	tr[ind].mx = tr[ind].tag = 0; if (l == r) return;
    	int mid = (l + r) >> 1;
    	build(ind<<1, l, mid); build(ind<<1|1, mid+1, r);
    }
    inline void pushdown(int ind) {
    	if (!tr[ind].tag) return; int v = tr[ind].tag; tr[ind].tag = 0;
    	tr[ind<<1].mx += v; tr[ind<<1].tag += v;
    	tr[ind<<1|1].mx += v; tr[ind<<1|1].tag += v;
    }
    void update(int ind, int l, int r, int x, int y, int v) {
    	if (x <= l && r <= y) {
    		tr[ind].mx += v; tr[ind].tag += v; return;
    	}
    	pushdown(ind); int mid = (l + r) >> 1;
    	if (x <= mid) update(ind<<1, l, mid, x, y, v);
    	if (mid < y) update(ind<<1|1, mid+1, r, x, y, v);
    	tr[ind].mx = max(tr[ind<<1].mx, tr[ind<<1|1].mx);
    }
    
    int main() {
    	read(n); read(m);
    	for (int i = 1; i <= n; i++) {
    		read(p[i].l); read(p[i].r);
    	}
    	sort(p + 1, p + n + 1, cmp);
    	fCuCcFk(); int ans = 0x3f3f3f3f;
    	build(1, 1, mx);
    	update(1, 1, mx, p[1].l, p[1].r, 1);
    	for (int i = 1, j = 1; i <= n; i++) {
    		while (j < n && tr[1].mx < m) {
    			j++;
    			update(1, 1, mx, p[j].l, p[j].r, 1);
    		}
    		if (tr[1].mx >= m) ans = min(ans, len[j] - len[i]);
    		update(1, 1, mx, p[i].l, p[i].r, -1);
    	}
    	printf("%d
    ", ans == 0x3f3f3f3f ? -1 : ans);
    	return 0;
    }
    
  • 相关阅读:
    PHP 学习轨迹
    beego 遇到的一些问题
    Fiddler 502问题
    SourceTree
    Trait
    PHP PSR 标准
    解决MySQL联表时出现字符集不一样
    Git 代码管理命令
    PHP 运行相关概念
    CentOS 7
  • 原文地址:https://www.cnblogs.com/ak-dream/p/AK_DREAM114.html
Copyright © 2011-2022 走看看