【luogu P1712区间】题解

【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_ilixri 。

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

求所有合法方案中最小的花费。如果不存在合法的方案,输出-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);
} 


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