BZOJ 4653: [Noi2016]区间

6 篇文章 0 订阅
1 篇文章 0 订阅

先离散化
然后按照长度从小到大排序
然后按长度顺序依次加入
用线段树维护当前的覆盖最多几条
然后如果当前有点的覆盖数等于 m 的就一直出队列,直到小于m,当前解的答案就是最后进队列的长度减最后出队列的长度
单调性可以证明,手画画搞搞也许可以吧。。。
年代太久远发现自己的代码都看不大懂了。。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define g getchar()
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
inline ll read(){
    ll x=0,f=1;char ch=g;
    for(;ch<'0'||ch>'9';ch=g)if(ch=='-')f=-1;
    for(;ch>='0'&&ch<='9';ch=g)x=x*10+ch-'0';
    return x*f;
}
inline void out(ll x){
    int a[25],t=0;
    if(x<0)putchar('-'),x=-x;
    for(;x;x/=10)a[++t]=x%10;
    for(int i=t;i;--i)putchar('0'+a[i]);
    if(t==0)putchar('0');
    putchar('\n');
}
struct re{int num,id;}a[4000005];
struct re2{int l,r,len;}b[4000005];
int cnt[4000005],m,fl,lll,n,now,hash[4000005],tou,ans,sum[4000005];
bool pd[4000005];
void ins(int k,int l,int r,int x,int y){
    if(l==x&&y==r){++cnt[k];++sum[k];return;}
    int mid=(l+r)>>1;
    if(x<=mid)ins(k<<1,l,mid,x,min(mid,y));
    if(y>mid)ins(k<<1|1,mid+1,r,max(mid+1,x),y);
    cnt[k]=max(cnt[k<<1],cnt[k<<1|1])+sum[k];
}
int query(int k,int l,int r,int x,int y){
    if(l==x&&r==y)return cnt[k];
    int mid=(l+r)>>1,ans=-inf;
    if(x<=mid)ans=query(k<<1,l,mid,x,min(y,mid));
    if(y>mid)ans=max(ans,query(k<<1|1,mid+1,r,max(x,mid+1),y));
    return ans;
}
void del(int k,int l,int r,int x,int y){
    if(l==x&&y==r){--cnt[k];--sum[k];return;}
    int mid=(l+r)>>1;
    if(x<=mid)del(k<<1,l,mid,x,min(mid,y));
    if(y>mid)del(k<<1|1,mid+1,r,max(x,mid+1),y);
    cnt[k]=max(cnt[k<<1],cnt[k<<1|1])+sum[k];
}
inline bool cmp1(re x,re y){return x.num==y.num?x.id<y.id:x.num<y.num;}
inline bool cmp2(re2 x,re2 y){return x.len<y.len;}
inline int cal(int i,int j){return hash[b[j].r]-hash[b[j].l]-hash[b[i].r]+hash[b[i].l];}
int main(){
    n=read(),m=read();
    for(int i=1;i<=n;++i){
        int x=read(),y=read();
        a[i*2-1]=(re){x,i};a[i*2]=(re){y,i};
        b[i]=(re2){x,y,y-x};
    }
    sort(a+1,a+1+n*2,cmp1);
    now=1;
    hash[1]=a[1].num;
    for(int i=1;i<=2*n;++i){
        if(a[i].num!=hash[now]){
            hash[++now]=a[i].num;
            if(!pd[a[i].id])
            b[a[i].id].l=now,pd[a[i].id]=1;
            else b[a[i].id].r=now;
        }else if(!pd[a[i].id])b[a[i].id].l=now,pd[a[i].id]=1;else b[a[i].id].r=now;
    }//没记错的话,到这里是离散化
    ans=inf;
    sort(b+1,b+1+n,cmp2);tou=1;
    for(int i=0;i<n||cnt[1]==m&&i==n;){
        for(;cnt[1]<m&&i<n;)++i,ins(1,1,now,b[i].l,b[i].r);
        for(;cnt[1]==m&&i<=n;)
        ans=min(ans,cal(tou,i)),del(1,1,now,b[tou].l,b[tou].r),++tou;
    }
    //cnt是线段树的统计数组,cnt1即为根,因为维护了最大值,查询复杂度O(1)
    out(ans<inf?ans:-1);
    return 0;
}

upd:
今儿又打了一遍
迷之优化一发艹到 rank3 。。
rank4 也是我嘿嘿嘿
好像是把画蛇添足的哈希去掉了直接预处理求出区间长度
长度也减到了66行


代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#define g getchar()
#define ll long long
#define inf 0x3f3f3f3f
#define rep(i,n) for(int i=1;i<=n;++i)
#define fi(i,j,k) for(int i=j;i<=k;++i)
#define di(i,j,k) for(int i=j;i>=k;--i)
using namespace std;
inline ll read(){
    ll x=0,f=1;char ch=g;
    for(;ch<'0'||ch>'9';ch=g)if(ch=='-')f=-1;
    for(;ch>='0'&&ch<='9';ch=g)x=x*10+ch-'0';
    return x*f;
}
inline void out(ll x){
    int a[25],wei=0;
    if(x<0)putchar('-'),x=-x;
    for(;x;x/=10)a[++wei]=x%10;
    if(wei==0){puts("0");return;}
    for(int j=wei;j>=1;--j)putchar('0'+a[j]);
    putchar('\n');
}
struct tr{int sum,mx;}t[2500005];
struct re{int l,r,len;}a[500005];
struct re2{int id,p;}b[1000005];
int n,m,cnt,now,np,ans,ansl;
bool pd[500005];
bool cmp1(re2 x,re2 y){return x.p<y.p;}
bool cmp2(re x,re y){return x.len>y.len;}
void ins(int k,int x,int y,int l,int r,int opt){
    if(x==l&&y==r){t[k].mx+=opt;t[k].sum+=opt;return;}
    int mid=(l+r)>>1;
    if(x<=mid)ins(k<<1,x,min(mid,y),l,mid,opt);
    if(y>mid)ins(k<<1|1,max(mid+1,x),y,mid+1,r,opt);
    t[k].mx=max(t[k<<1].mx,t[k<<1|1].mx)+t[k].sum;
}
int main(){
    n=read();m=read();
    rep(i,n){
        a[i].l=read(),a[i].r=read();a[i].len=a[i].r-a[i].l;
        b[++cnt]=(re2){i,a[i].l};b[++cnt]=(re2){i,a[i].r};
    }
    sort(b+1,b+1+cnt,cmp1);
    for(int i=1;i<=cnt;){
        ++now;np=b[i].p;
        for(;b[i].p==np&&i<=cnt;++i){
            if(pd[b[i].id])a[b[i].id].r=now;
            else pd[b[i].id]=1,a[b[i].id].l=now;
        }
    }
    sort(a+1,a+1+n,cmp2);
    ans=inf;
    for(int tou=1,wei=1;wei>=tou&&wei<=n;++wei){
        ins(1,a[wei].l,a[wei].r,1,now,1);
        ansl=-1;
        for(;t[1].mx>=m;++tou){
            ansl=tou;
            ins(1,a[tou].l,a[tou].r,1,now,-1);
        }
        if(ansl!=-1)ans=min(a[ansl].len-a[wei].len,ans);
    }
    out(ans==inf?-1:ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值