bzoj 4653: [Noi2016]区间 (线段树)

来源:互联网 发布:ps是什么软件 编辑:程序博客网 时间:2024/05/17 01:12

题目描述

传送门

题目大意:从n个区间中选出m个区间,使m个区间至少有一个位置重合。且选中的区间中区间最大长度与最小长度的差最小。

题解

将区间按照大小从小到大排序,依次将区间加入线段树中。如果覆盖次数最大的位置>=m,就从小区间开始弹出,直到覆盖次数最大的位置

代码

#include<iostream>#include<cstdio>#include<algorithm>#include<cmath>#include<cstring>#define N 1000003using namespace std;int tr[N*4],delta[N*4],n,m,c[N],cnt;struct data{    int l,r,x;}a[N];void update(int now){    tr[now]=max(tr[now<<1],tr[now<<1|1]);}void change(int now,int v){    tr[now]+=v;    delta[now]+=v;}void pushdown(int now){    if (delta[now]){        change(now<<1,delta[now]);        change(now<<1|1,delta[now]);        delta[now]=0;    }}void query(int now,int l,int r,int ll,int rr,int v){    if (ll<=l&&r<=rr) {        change(now,v);        return;    }    int mid=(l+r)/2;    pushdown(now);    if (ll<=mid) query(now<<1,l,mid,ll,rr,v);    if (rr>mid) query(now<<1|1,mid+1,r,ll,rr,v);    update(now);}int cmp(data a,data b){    return a.x<b.x; }int main(){    freopen("a.in","r",stdin);//  freopen("my.out","w",stdout);    scanf("%d%d",&n,&m);    for (int i=1;i<=n;i++){        scanf("%d%d",&a[i].l,&a[i].r);        a[i].x=a[i].r-a[i].l;        c[++cnt]=a[i].l; c[++cnt]=a[i].r;    }    sort(c+1,c+cnt+1);    cnt=unique(c+1,c+cnt+1)-c-1;    for (int i=1;i<=n;i++)     a[i].l=lower_bound(c+1,c+cnt+1,a[i].l)-c,     a[i].r=lower_bound(c+1,c+cnt+1,a[i].r)-c;    //for (int i=1;i<=n;i++) cout<<a[i].l<<" "<<a[i].r<<endl;    sort(a+1,a+n+1,cmp);    int head=1; int ans=2000000000;    for (int i=1;i<=n;i++){        query(1,1,cnt,a[i].l,a[i].r,1);        if (tr[1]<m) continue;         while (true){            query(1,1,cnt,a[head].l,a[head].r,-1);            head++;            if (tr[1]<m) break;        }        int t=max(1,head-1);        ans=min(ans,a[i].x-a[t].x);    }    if (ans==2000000000) printf("-1\n");    else printf("%d\n",ans);}
原创粉丝点击