首页 > 其他 > 详细

【luogu P1712区间】题解

时间:2019-10-14 21:38:50      阅读:132      评论:0      收藏:0      [点我收藏+]

【NOI2016】区间

题目描述

在数轴上有NN 个闭区间 [l_1,r_1],[l_2,r_2],...,[l_n,r_n][l1?,r1?],[l2?,r2?],...,[ln?,rn?] 。现在要从中选出MM个区间,使得这MM 个区间共同包含至少一个位置。换句话说,就是使得存在一个 xx ,使得对于每一个被选中的区间[l_i,r_i][li?,ri?] ,都有 l_i≤x≤r_ili?xri? 。

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

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

输入格式

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

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

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

输出格式

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

输入输出样例

输入
6 3
3 5
1 2
3 4
2 2
1 5
1 4
输出
2

这道题呢,题目说要用长度最大的区间减去长度最小的区间,正好本来序列就没什么顺序,那我们给区间排个序吧,这样可以方便统计答案。
排完序做什么呢?一定是统计每个点出现的次数吧。对于这些个区间,修改区间上点出现的次数,线段树是常规操作吧。
我们用线段树修改所有点出现的次数的最大值,这样,当枚举到i时,加上区间i对点出现次数的贡献,此时tree[1]如果等于m,这说明一个可能的答案产生了。
我们枚举刚刚统计过的区间j,把他的贡献删掉,线段树区间减。
看看tree[1]是否发生变化。
如果tree[1]变小,那说明这是对这个答案有影响的长度最小的区间了。更新答案ans=min(ans,sec[i].len-sec[j].len)。
想想为什么可以删掉j这个区间?因为i后面的区间一定比i长,减去j后产生的答案一定会更大,因此完全不用考虑j对i后面区间的影响。
既然j被删掉了,那么下一个区间就是j+1了,用head标记这个,当下一个答案产生时,从head开始枚举。
注意到原区间坐标很大,引入离散化是必要的。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ls k<<1
#define rs k<<1|1
#define min(x,y) (x<y?x:y)
#define max(x,y) (x>y?x:y)
using namespace std;
const int N=550000; 
int n,m,ans=0x7fffffff;
int temp[N<<4];
int tree[N<<4],tag[N<<4];
struct node{
    int l,r,len;
}sec[N];
inline bool cmp(node x,node y){
    return x.len<y.len;
}
inline void read(int &x){
    x=0;
    char ch=getchar();
    while(ch<0||ch>9) ch=getchar();
    while(ch>=0&&ch<=9) x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
}
inline void updata(int k,int x,int y){
    int mid=x+y>>1;
    tree[ls]+=tag[k];
    tree[rs]+=tag[k];
    tag[ls]+=tag[k];
    tag[rs]+=tag[k];
    tag[k]=0;
}
inline void change(int k,int x,int y,int l,int r,int val){
    if(x>r||y<l) return;
    if(x>=l&&y<=r){
        tree[k]=tree[k]+val;
        tag[k]+=val;
        return;
    }
    if(tag[k]) updata(k,x,y);
    int mid=x+y>>1;
    change(ls,x,mid,l,r,val);
    change(rs,mid+1,y,l,r,val);
    tree[k]=max(tree[ls],tree[rs]);
}
int main()
{
    int i,j,x,y,t;
    int cnt=0;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++){
        read(sec[i].l),read(sec[i].r);
        sec[i].len=sec[i].r-sec[i].l;
        temp[++cnt]=sec[i].l;
        temp[++cnt]=sec[i].r;
    }
    sort(sec+1,sec+n+1,cmp);
    sort(temp+1,temp+1+cnt);
    cnt=unique(temp+1,temp+1+cnt)-temp-1;
    for(i=1;i<=n;i++){
        sec[i].l=lower_bound(temp+1,temp+1+cnt,sec[i].l)-temp;
        sec[i].r=lower_bound(temp+1,temp+1+cnt,sec[i].r)-temp;
    }
    int head=0;
    for(i=1;i<=n;i++){
        x=sec[i].l,y=sec[i].r;
        change(1,1,cnt,x,y,1);
        if(tree[1]>=m)
            for(j=head;j<=i;j++){
                x=sec[j].l,y=sec[j].r;
                change(1,1,cnt,x,y,-1);//这时线段树的长度应该是1~cnt,而不是n
                if(tree[1]<m){
                    head=j+1;
                    ans=min(ans,sec[i].len-sec[j].len);
                    break;
                }
            }
    }
    if(ans==0x7fffffff) ans=-1;//不要忘记判断这个。
    printf("%d",ans);
} 

 



 

【luogu P1712区间】题解

原文:https://www.cnblogs.com/quitter/p/11674267.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!