【APIO2018】新家(线段树)

【APIO2018】新家(线段树)

题面

UOJ
洛谷
BZOJ

题解

论比赛时想不到二分的危害,就只能Cu滚粗

既然不要在线,那么考虑离线做法。
既然时间是区间,那么显然按照时间顺序处理答案。
显然答案具有可二分性,那么对于当前位置而言,我们唯一要确定的就是\([x-mid,x+mid]\)区间内是否所有种类的商店都至少出现过了一次。
因为离线之后按照时间处理,因此已经没有了时间这一维,只剩下了位置和种类两个东西。那么问题就是二维数点,但是因为要求每个种类只数一次,所以似乎没法直接上树套树。
既然要维护这个东西,显然需要额外记录当前颜色的前驱位置,假装为\(pre_i\)
发现这样子仍然需要依次考虑每一个颜色,那么我们换一种方法。\(pre_i\)记录的既然是前驱位置,那么考虑\([x+mid+1,inf)\)的每个种类的商店的前驱位置,如果前驱不在\([x-mid,x+mid]\)的区间内,即证明区间内不存在这个种类的商店。既然多个商店可以在同一个位置,所以\(pre_i\)就记录和在\(i\)位置的所有商店中的最小前驱位置。这样子转为对于\((i,pre_i)\)进行二维数点,并且只需要统计点数。
那么考虑如何维护\(pre_i\)的值就好了。
一个显而易见的想法就是对于每个种类开一个\(set\)维护其出现的所有位置。首先维护一下无解的情况,然后因为可能一种颜色并没有在\([x+mid+1,inf)\)范围内出现,所以再额外维护一下每种颜色最后一个位置的最小值就好了。然而实际写的时候并不需要维护最小位置,可以强行在\(inf\)位置上给每个种类强行加一个进来,再在\(-inf\)位置上强制加一个,这样子就不存在没有前驱或者后继的情况了。
最后的二分可以选择在线段树上二分,二分右端点最终直接减一下就是答案了。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<set>
using namespace std;
#define ll long long
#define MAX 300300
#define inf 1000000000
inline int read()
{
    int x=0;bool t=false;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
int n,K,Q,Typecnt,ans[MAX];
struct Work
{
    int y;//time
    int x,t;//position,type
    int opt;//add/del/query
}p[MAX*3];
int tot;
bool operator<(Work a,Work b){if(a.y!=b.y)return a.y<b.y;return a.opt<b.opt;}
multiset<int>::iterator it,itt;
multiset<int> S[MAX],tt[MAX*20];
struct Node{int mn,ls,rs;}t[MAX*20];
int TOT,rt;
void Modify(int &x,int l,int r,int p,int Add,int Del)
{
    if(!x)x=++TOT;
    if(l==r)
    {
        if(Add)tt[x].insert(Add);
        if(Del)tt[x].erase(tt[x].find(Del));
        t[x].mn=(tt[x].empty()?inf:*tt[x].begin());
        return;
    }
    int mid=(l+r)>>1;
    if(p<=mid)Modify(t[x].ls,l,mid,p,Add,Del);
    else Modify(t[x].rs,mid+1,r,p,Add,Del);
    t[x].mn=min(t[t[x].ls].mn,t[t[x].rs].mn);
}
int Query(int p)
{
    int l=0,r=inf,x=rt,mn=inf;
    while(l<r)
    {
        int mid=(l+r)>>1,pos=min(t[t[x].rs].mn,mn);
        if(p>mid||(mid-p<p-pos))l=mid+1,x=t[x].rs;
        else r=mid,x=t[x].ls,mn=pos;
    }
    return l-p;
}
int main()
{
    n=read();K=read();Q=read();t[0].mn=inf;
    for(int i=1;i<=n;++i)
    {
        int x=read(),t=read(),a=read(),b=read();
        p[++tot]=(Work){a,x,t,1};
        p[++tot]=(Work){b+1,x,t,-1};
    }
    for(int i=1;i<=Q;++i)
    {
        int x=read(),y=read();
        p[++tot]=(Work){y,x,i,2};
    }
    sort(&p[1],&p[tot+1]);
    for(int i=1;i<=K;++i)S[i].insert(inf),S[i].insert(-inf);
    for(int i=1;i<=K;++i)Modify(rt,0,inf,inf,-inf,0);
    for(int i=1;i<=tot;++i)
    {
        int opt=p[i].opt;
        if(opt==1)//Add
        {
            it=itt=S[p[i].t].upper_bound(p[i].x);
            --itt;
            Modify(rt,0,inf,*it,p[i].x,*itt);
            Modify(rt,0,inf,p[i].x,*itt,0);
            if(S[p[i].t].size()==2)++Typecnt;
            S[p[i].t].insert(p[i].x);
        }
        else if(opt==-1)//Del
        {
            it=itt=S[p[i].t].upper_bound(p[i].x);
            --itt;--itt;
            Modify(rt,0,inf,*it,*itt,p[i].x);
            Modify(rt,0,inf,p[i].x,0,*itt);
            S[p[i].t].erase(S[p[i].t].find(p[i].x));
            if(S[p[i].t].size()==2)--Typecnt;
        }
        else ans[p[i].t]=Typecnt<K?-1:Query(p[i].x);
    }
    for(int i=1;i<=Q;++i)printf("%d\n",ans[i]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/cjyyb/p/10201636.html