「NOI2016」区间 解题报告

「NOI2016」区间

最近思维好僵硬啊...

一上来就觉得先把区间拆成两个端点进行差分,然后扫描位置序列,在每个位置维护答案,用数据结构维护当前位置的区间序列,但是不会维护。

于是想研究性质,想到为什么要拿区间长度做权值呢,难道是有一些性质吗

于是思考了很久区间长度的性质,猜了一些sb结论,比如什么一个区间只有加入时和删除时的贡献算一下就可以了之类的...

全错了然后就自闭了...

然后想什么钦定最大值,然后询问位置区间,然后我发现线段树每个点要挂一个单调队列(事实上把单调队列从线段树上拿下来就是正解了,我当时一点都没想到),然后又自闭了

然后想干脆钦定最大最小值,反正只有\(n^2\)种,然后我们需要查询长度在最大最小值之间的区间,然后我们看一下这些区间的区间并有没有一个位置的值比\(m\)大,区间并可以染色,然后查询最大值

这样可以拿树套树暴力搞了,好像很对

然后发现如果扫描的最大值最小值两个指针的话,移动是\(O(n)\)的,好像叫尺取法把,然后就可以只维护一个线段树了。


Code:

#include <cstdio>
#include <cctype>
#include <algorithm>
using std::max;
const int SIZE=1<<21;
char ibuf[SIZE],*iS,*iT;
#define gc() (iS==iT?(iT=(iS=ibuf)+fread(ibuf,1,SIZE,stdin),iS==iT?EOF:*iS++):*iS++)
//#define gc() getchar()
template <class T>
void read(T &x)
{
    x=0;char c=gc();
    while(!isdigit(c)) c=gc();
    while(isdigit(c)) x=x*10+c-'0',c=gc();
}
void ckmin(int &x,int y){x=x<y?x:y;}
const int inf=0x3f3f3f3f;
const int N=5e5+10;
int n,m,k,saki[N<<1];
struct _toki
{
    int l,r,val;
    bool friend operator <(_toki a,_toki b){return a.val<b.val;}
}toki[N];
int mx[N<<3],tag[N<<3];
#define ls id<<1
#define rs id<<1|1
void pushdown(int id)
{
    if(tag[id])
    {
        mx[ls]+=tag[id],tag[ls]+=tag[id];
        mx[rs]+=tag[id],tag[rs]+=tag[id];
        tag[id]=0;
    }
}
void upt(int id,int L,int R,int l,int r,int d)
{
    if(r<L||l>R) return;
    if(l<=L&&R<=r)
    {
        mx[id]+=d;
        tag[id]+=d;
        return;
    }
    pushdown(id);
    int Mid=L+R>>1;
    upt(ls,L,Mid,l,r,d),upt(rs,Mid+1,R,l,r,d);
    mx[id]=max(mx[ls],mx[rs]);
}
int qry(int id,int L,int R,int l,int r)
{
    if(r<L||l>R) return 0;
    if(l<=L&&R<=r) return mx[id];
    pushdown(id);
    int Mid=L+R>>1;
    return max(qry(ls,L,Mid,l,r),qry(rs,Mid+1,R,l,r));
}
int main()
{
    //freopen("data.in","r",stdin);
    //freopen("data.out","w",stdout);
    read(n),read(k);
    for(int i=1;i<=n;i++)
    {
        read(toki[i].l),read(toki[i].r);
        saki[++m]=toki[i].l,saki[++m]=toki[i].r;
        toki[i].val=toki[i].r-toki[i].l;
    }
    std::sort(saki+1,saki+1+m);
    m=std::unique(saki+1,saki+1+m)-saki-1;
    for(int i=1;i<=n;i++)
    {
        toki[i].l=std::lower_bound(saki+1,saki+1+m,toki[i].l)-saki;
        toki[i].r=std::lower_bound(saki+1,saki+1+m,toki[i].r)-saki;
    }
    std::sort(toki+1,toki+1+n);
    int l=1,r=1,ans=inf;
    while(r<=n)
    {
        upt(1,1,m,toki[r].l,toki[r].r,1);
        while(qry(1,1,m,toki[r].l,toki[r].r)>=k)
        {
            ckmin(ans,toki[r].val-toki[l].val);
            upt(1,1,m,toki[l].l,toki[l].r,-1);
            ++l;
        }
        ++r;
    }
    if(ans==inf) puts("-1");
    else printf("%d\n",ans);
    return 0;
}

2019.5.30

转载于:https://www.cnblogs.com/butterflydew/p/10950152.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值