【BZOJ4653】区间,离散化+线段树

Time:2016.08.09
Author:xiaoyimi
转载注明出处谢谢


传送门
思路:
当时打同步赛的时候
花了3h去做T3提答
大概玩了50分
回来看T1竟然是懵逼状态……
这里写图片描述
想了一会,满脑子都是离散化后差分前缀和处理blabla
但不知道怎么处理区间覆盖大于m时答案的最优性
(为什么当时连按区间大小排序的思路都没有想到?!)
其实按照区间大小排序,那么答案一定是a[l],a[l+1]..a[r]这些区间的并集,所以我们把i从1枚举到n,同时j=1,操作为
1.每次覆盖一下区间a[j],看一下有没有被覆盖大于等于m的点,如果有就去拿最长区间-最小区间与ans比较,否则++j,继续看一下有没有覆盖大于等于m的点;
2.把a[i]区间去除
覆盖,去除,检查大于等于m的点都可以用线段树的区间覆盖,单点查询最大值解决
复杂度 O(nlog2n)
常数主要在离散化那里
希望明年的自己不会滚粗
……
……
……
但前提是要先进省队!!!
……
……
……
更大的前提是把3个月后的NOIP考好!!!
这里写图片描述
代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#define M 500002
using namespace std;
int in()
{
    int t=0;char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9') t=(t<<3)+(t<<1)+ch-48,ch=getchar();
    return t;
}
int a[M<<1],lazy[M<<3],tr[M<<3];
int n=in(),m=in();
struct node
{
    int l,r,len;
    bool operator <(node other)const {return len<other.len;}
}q[M];
void pushdown(int rt)
{
    if (!lazy[rt]) return;
    lazy[rt<<1]+=lazy[rt];
    lazy[rt<<1|1]+=lazy[rt];
    tr[rt<<1]+=lazy[rt];
    tr[rt<<1|1]+=lazy[rt];
    lazy[rt]=0; 
}
void update(int rt,int begin,int end,int L,int R,int val)
{
    if (L<=begin&&end<=R)
    {
        tr[rt]+=val;
        lazy[rt]+=val;
        return;
    }
    pushdown(rt);
    int mid=begin+end>>1;
    if (mid>=L) update(rt<<1,begin,mid,L,R,val);
    if (mid<R)  update(rt<<1|1,mid+1,end,L,R,val);
    tr[rt]=max(tr[rt<<1],tr[rt<<1|1]);
}
main()
{
    for (int i=1;i<=n;i++)
        q[i].l=in(),
        q[i].r=in(),
        q[i].len=q[i].r-q[i].l,
        a[++a[0]]=q[i].l,
        a[++a[0]]=q[i].r;
    sort(a+1,a+a[0]+1);
    a[0]=unique(a+1,a+a[0]+1)-(a+1);
    for (int i=1;i<=n;i++)
        q[i].l=lower_bound(a+1,a+a[0]+1,q[i].l)-a,
        q[i].r=lower_bound(a+1,a+a[0]+1,q[i].r)-a;
    sort(q+1,q+n+1);
    int ans=1<<30,t=0;
    for (int i=1;i<=n;i++)
    {
        while (t<n&&tr[1]<m)
            t++, 
            update(1,1,a[0],q[t].l,q[t].r,1);
        if (tr[1]>=m) ans=min(ans,q[t].len-q[i].len);
        else break;
        update(1,1,a[0],q[i].l,q[i].r,-1);
    }
    printf("%d",ans==1<<30?-1:ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值