• [bzoj4653] [NOI2016]区间


    Description

    在数轴上有 n个闭区间 [l1,r1],[l2,r2],...,[ln,rn]。现在要从中选出 m 个区间,使得这 m个区间共同包含至少一个位置。换句话说,就是使得存在一个 x,使得对于每一个被选中的区间 [li,ri],都有 li≤x≤ri。

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

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

    Input

    第一行包含两个正整数 n,m用空格隔开,意义如上文所述。保证 1≤m≤n

    接下来 n行,每行表示一个区间,包含用空格隔开的两个整数 li 和 ri 为该区间的左右端点。

    N<=500000,M<=200000,0≤li≤ri≤10^9

    Output

    只有一行,包含一个正整数,即最小花费。

    Sample Input

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

    Sample Output

    2
    

    Solution

    由于答案是最大的区间减最小的区间,而且和顺序没关系,所以可以按区间长度排序。

    按顺序加入,显然当有一个点的覆盖次数大于(m)时,就不加了。

    然后对于已经加进去的,从小到大删除,直到覆盖次数全都小于(m)时,最后一个删掉的和当前加入的最大的区间肯定可以构成一个当前最优的方案。

    然后开个线段树,拿两个指针指一下,模拟下上面的过程就行了。

    #include<bits/stdc++.h>
    using namespace std;
    
    void read(int &x) {
    	x=0;int f=1;char ch=getchar();
    	for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
    	for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
    }
    
    void print(int x) {
    	if(x<0) putchar('-'),x=-x;
    	if(!x) return ;print(x/10),putchar(x%10+48);
    }
    void write(int x) {if(!x) putchar('0');else print(x);putchar('
    ');}
    
    const int maxn = 5e6+1;
    
    struct node {
    	int l,r,len;
    	bool operator < (const node &rhs) const {return len<rhs.len;} 
    }in[maxn<<1];
    int a[maxn],b[maxn],n,m,tot,head,tail;
    
    #define ls p<<1
    #define rs p<<1|1
    #define mid ((l+r)>>1)
    
    struct Segment_Tree {
    	int tr[maxn],tag[maxn];
    	void push_tag(int p,int v) {tr[p]+=v,tag[p]+=v;}
    	void update(int p) {tr[p]=max(tr[ls],tr[rs]);}
    	void pushdown(int p) {
    		if(tag[p]) push_tag(ls,tag[p]),push_tag(rs,tag[p]),tag[p]=0;
    	}
    	void modify(int p,int l,int r,int x,int y,int v) {
    		if(x<=l&&r<=y) return push_tag(p,v),void();
    		pushdown(p);
    		if(x<=mid) modify(ls,l,mid,x,y,v);
    		if(y>mid) modify(rs,mid+1,r,x,y,v);
    		update(p);
    	}
    }SGT;
    
    #undef ls
    #undef rs
    #undef mid
    
    int main() {
    	read(n),read(m);
    	for(int i=1;i<=n;i++) read(in[i].l),read(in[i].r),in[i].len=in[i].r-in[i].l;
    	sort(in+1,in+n+1);
    	for(int i=1;i<=n;i++) a[++tot]=in[i].l,a[++tot]=in[i].r;
    	sort(a+1,a+tot+1);int M=unique(a+1,a+tot+1)-a-1;
    	for(int i=1;i<=n;i++) {
    		in[i].l=lower_bound(a+1,a+M+1,in[i].l)-a;
    		in[i].r=lower_bound(a+1,a+M+1,in[i].r)-a;
    	}
    	int ans=1e9;
    	while(tail<n) {
    		while(tail<n&&SGT.tr[1]<m) tail++,SGT.modify(1,1,n*2,in[tail].l,in[tail].r,1);
    		if(SGT.tr[1]<m) break;
    		while(head<=tail&&SGT.tr[1]>=m) head++,SGT.modify(1,1,n*2,in[head].l,in[head].r,-1);
    		ans=min(ans,in[tail].len-in[head].len);
    	}
    	if(ans!=1e9) write(ans);else puts("-1");
    	return 0;
    }
    
  • 相关阅读:
    Java学习路线:day1 Java语言概述
    Java学习路线:day5 Java基本语法(下)2
    Java学习路线:day4 Java基本语法(下)
    Python笔记_第四篇_高阶编程_GUI编程之Tkinter_2.控件类
    Python笔记_第四篇_高阶编程_GUI编程之Tkinter_1.使用Python进行GUI编程的概述
    Python笔记_第三篇_面向对象_9.Python中的"get"和"set"方法(@property和@.setter)
    Python笔记_第三篇_面向对象_8.对象属性和类属性及其动态添加属性和方法
    Python笔记_第三篇_面向对象_7.多态
    Python笔记_第三篇_面向对象_6.继承(单继承和多继承)
    Python笔记_第三篇_面向对象_5.一个关于类的实例(人开枪射击子弹)
  • 原文地址:https://www.cnblogs.com/hbyer/p/10172874.html
Copyright © 2020-2023  润新知