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

    Description

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

    Input

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

    Output

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

    Sample Input

    6 3

    3 5

    1 2

    3 4

    2 2

    1 5

    1 4

    Sample Output

    2


    想法

    还是挺巧的(或是说我太弱了)。
    一开始我的想法是枚举那 (m) 个区间都包含的点 (x) ,在包含 (x) 的那些区间中找答案,但发现这样并不好做。

    于是换一个角度,从答案出发。
    在一个合法方案中,设选中的区间最长与最短分别为 (mx)(mn)
    那么如果将长度在 ([mn,mx]) 中的所有区间都选上,显然是满足条件的。
    不妨将区间按长度从小到大排序,每次选连续的一段区间,只要这一段区间将某个点覆盖大于等于 (m) 次,就满足条件。

    线段树+双指针(尺取法)维护就可以了~


    代码

    别忘了在不存在方案时输出-1!

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    
    using namespace std;
    
    int read(){
    	int x=0;
    	char ch=getchar();
    	while(!isdigit(ch)) ch=getchar();
    	while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
    	return x;
    }
    
    const int N = 500005;
    
    struct data{
    	int l,r,len;
    	bool operator < (const data &b) const { return len<b.len; }
    }d[N];
    int n,m;
    int b[N*2],tot;
    
    struct tree{
    	tree *ch[2];
    	int mx,lazy;
    }pool[N*4],*root;
    int cnt;
    void build(tree *p,int l,int r){
    	p->mx=p->lazy=0;
    	if(l==r) return;
    	int mid=(l+r)>>1;
    	build(p->ch[0]=&pool[++cnt],l,mid);
    	build(p->ch[1]=&pool[++cnt],mid+1,r);
    }
    void pushdown(tree *p){
    	if(!p->lazy) return;
    	p->ch[0]->lazy+=p->lazy; p->ch[0]->mx+=p->lazy;
    	p->ch[1]->lazy+=p->lazy; p->ch[1]->mx+=p->lazy;
    	p->lazy=0;
    }
    void add(tree *p,int l,int r,int L,int R,int c){
    	if(l==L && r==R) { p->lazy+=c; p->mx+=c; return; }
    	pushdown(p);
    	int mid=(l+r)>>1;
    	if(R<=mid) add(p->ch[0],l,mid,L,R,c);
    	else if(L>mid) add(p->ch[1],mid+1,r,L,R,c);
    	else{
    		add(p->ch[0],l,mid,L,mid,c);
    		add(p->ch[1],mid+1,r,mid+1,R,c);
    	}
    	p->mx=max(p->ch[0]->mx,p->ch[1]->mx);
    }
    
    int main()
    {
    	n=read(); m=read();
    	for(int i=1;i<=n;i++){
    		d[i].l=read(); d[i].r=read(); d[i].len=d[i].r-d[i].l;
    		b[++tot]=d[i].l; b[++tot]=d[i].r;
    	}
    	
    	sort(b+1,b+1+tot);
    	tot=unique(b+1,b+1+tot)-b-1;
    	for(int i=1;i<=n;i++){
    		d[i].l=lower_bound(b+1,b+1+tot,d[i].l)-b;
    		d[i].r=lower_bound(b+1,b+1+tot,d[i].r)-b;
    	}
    	sort(d+1,d+1+n);
    	
    	build(root=&pool[++cnt],1,tot);
    	add(root,1,tot,d[1].l,d[1].r,1);
    	int l=1,r=1,ans=1000000005;
    	for(;l<=n;l++){
    		if(l!=1) add(root,1,tot,d[l-1].l,d[l-1].r,-1);
    		for(;root->mx<m && r<n;){
    			r++;
    			add(root,1,tot,d[r].l,d[r].r,1);
    		}
    		if(root->mx<m) break;
    		ans=min(ans,d[r].len-d[l].len);
    	}
    	if(ans!=1000000005) printf("%d
    ",ans);
    	else printf("-1
    ");
    	
    	return 0;
    }
    
    既然选择了远方,便只顾风雨兼程
  • 相关阅读:
    miniport hook ethFilterDprIndicateReceivePacket 接收拦截时包处理问题
    XRename(文件文件夹超级重命名工具)简介
    正则表达式测试工具
    很好看的表格样式
    FillForms 1.2.9 preliminarily reviewed
    CSDN分页ID提取工具(vb编写)
    html表格样式等整理
    备忘录
    巧用正则巅峰采集黄金白银大盘价信息
    2011年个人奋斗目标
  • 原文地址:https://www.cnblogs.com/lindalee/p/11350287.html
Copyright © 2011-2022 走看看