【LOJ2585】新家(APIO2018)-线段树+multiset

测试地址:新家
做法:本题需要用到线段树+multiset。
这题题意有点复杂,先理一下:数轴上有一些带颜色的点,每个点会在某个特定的时间段出现,若干个询问,每次询问某个时刻上离一个坐标距离最远的颜色的距离。坐标和颜色的距离定义为,该坐标与当前出现的所有该颜色的点的最小距离。
理完了题意,就可以想题了。首先因为没有强制在线,所以显然将操作按时间排序,而且把某个时间段出现某个点变成两个操作:某时刻新增一个点,又在某时刻将其删除。于是我们现在要想怎么找到最远的距离。
我们发现直接找到这个距离非常困难,因此想到二分答案,转化为判定性问题,即判定某个距离内存不存在所有的颜色。这时候我们要想到,点是可以随时修改的,所以肯定不能用主席树这种不能动的结构,那怎么办呢?我们给每一个点i定义一个前驱pre(i),表示和它同颜色的,它左边离它最近的点。注意到,如果存在一个x>R,使得pre(x)<L,就表示x点的颜色没有在区间[L,R]内出现,所以我们只要求出[R+1,inf)内的最小pre值,再和L比较,就可以知道区间内存不存在所有颜色了。
要注意的是,由于区间内出现的一个点有可能是该种颜色的最右侧的点,因此我们在数轴最右端插入一个点,这个点拥有全部的颜色,这样就可以处理这种情况了。于是我们只要对每种颜色开一个multiset存一下位置,以便找前驱和后继。
还有一个重要的问题,一个坐标上有可能有很多点,这时候我们光用坐标,颜色或者pre都无法区别它们,所以只能对每个离散化后的坐标位置存一个multiset来表示这个位置上的点集的所有pre值。而又因为这个问题,我们需要更严格地定义前驱和后继。一般认为,插入时,该点的后继的前驱就是插入后该点的前驱。这样的话,后继就是upper_bound,前驱就是upper_bound前面的一个。而在删除时略有不同,因为upper_bound的前面一个位置上是自己,所以还要再往前一个位置才是它真正的前驱。
于是我们用一堆multiset维护每个点的pre值,在涉及修改时,在线段树上单点修改,那么就可以O(logn)完成一次答案的判定了。因此总的时间复杂度为O(nlog2n)
当然,如果你想继续优化,方法还是有的,注意到目前时间复杂度的瓶颈在于二分答案再在线段树上询问,你可以小推一下,改成直接在线段树上二分,这样时间复杂度就变为O(nlogn)了,但是我比较懒,就不写了,留作习题(喂喂喂…)。
以下是本人代码:

#include <bits/stdc++.h>
using namespace std;
const int N=300010;
const int inf=100000010;
int n,k,q,tot,pos[N],ans[N];
int seg[N<<2];
struct oper
{
    int v,x,t,type; //v:time,x:position,t:color,type:operation_type
}op[N<<2];
struct forsort
{
    int id,val;
}f[N];
multiset<int> pointS[N],colorS[N];
multiset<int>::iterator it;

bool cmp(forsort a,forsort b)
{
    return a.val<b.val;
}

bool cmpop(oper a,oper b)
{
    if (a.v==b.v) return a.type<b.type;
    else return a.v<b.v;
}

void init()
{
    scanf("%d%d%d",&n,&k,&q);
    for(int i=1;i<=n;i++)
    {
        int p,t,a,b;
        scanf("%d%d%d%d",&p,&t,&a,&b);
        int x=(i<<1)-1,y=(i<<1);
        op[x].v=a,op[x].t=t,op[x].type=0;
        op[y].v=b+1,op[y].t=t,op[y].type=1;
        f[i].id=i;
        f[i].val=p;
    }

    sort(f+1,f+n+1,cmp);
    tot=-1;
    for(int i=1;i<=n;i++)
    {
        if (i==1||f[i].val!=f[i-1].val)
            pos[++tot]=f[i].val;
        op[(f[i].id<<1)-1].x=tot;
        op[f[i].id<<1].x=tot;
    }
    pos[++tot]=inf;

    for(int i=1;i<=q;i++)
    {
        op[(n<<1)+i].type=2;
        scanf("%d%d",&op[(n<<1)+i].x,&op[(n<<1)+i].v);
        op[(n<<1)+i].t=i;
    }
    sort(op+1,op+(n<<1)+q+1,cmpop);

    for(int i=0;i<tot;i++)
    {
        pointS[i].insert(inf);
        //printf("insert %d %d\n",i,inf);
    }
    for(int i=1;i<=k;i++)
    {
        colorS[i].insert(-1),colorS[i].insert(tot);
        pointS[tot].insert(-1);
    }
}

void pushup(int no)
{
    seg[no]=min(seg[no<<1],seg[no<<1|1]);
}

void modify(int no,int l,int r,int x,int d)
{
    if (l==r) {seg[no]=d;return;}
    int mid=(l+r)>>1;
    if (x<=mid) modify(no<<1,l,mid,x,d);
    else modify(no<<1|1,mid+1,r,x,d);
    pushup(no);
}

int query(int no,int l,int r,int s,int t)
{
    if (l>=s&&r<=t) return seg[no];
    int mid=(l+r)>>1,ans=inf;
    if (s<=mid) ans=min(ans,query(no<<1,l,mid,s,t));
    if (t>mid) ans=min(ans,query(no<<1|1,mid+1,r,s,t));
    return ans;
}

bool check(int l,int r)
{
    int L,R,ans=inf;
    L=lower_bound(pos,pos+tot+1,l)-pos;
    if (L>tot) return 0;
    R=upper_bound(pos,pos+tot+1,r)-pos;
    R--;
    if (R>=tot) R=tot-1;
    if (L>R) return 0;

    ans=min(ans,query(1,0,tot,R+1,tot));
    return ans>=L;
}

void work()
{
    for(int i=1;i<=((tot+1)<<2);i++)
        seg[i]=inf;
    int cnt=0;
    for(int i=1;i<=(n<<1)+q;i++)
    {
        int p=op[i].x,t=op[i].t;
        int pre,nxt;
        if (op[i].type<2)
        {
            it=colorS[t].upper_bound(p);
            nxt=(*it);it--;pre=(*it);
            if (op[i].type==1&&pre==p) it--,pre=(*it);
        }
        if (op[i].type==0)
        {
            pointS[p].insert(pre);
            it=pointS[nxt].find(pre);
            pointS[nxt].erase(it);
            //printf("insert %d %d\n",p,pre);
            //printf("erase %d %d\n",nxt,pre);
            pointS[nxt].insert(p);
            //printf("insert %d %d\n",nxt,p);
            modify(1,0,tot,p,(*pointS[p].begin()));
            modify(1,0,tot,nxt,(*pointS[nxt].begin()));
            if (colorS[t].size()==2) cnt++;
            colorS[t].insert(p);
        }
        else if (op[i].type==1)
        {
            it=pointS[p].find(pre);
            pointS[p].erase(it);
            pointS[nxt].insert(pre);
            //printf("erase %d %d\n",p,pre);
            //printf("insert %d %d\n",nxt,pre);
            it=pointS[nxt].find(p);
            pointS[nxt].erase(it);
            //printf("erase %d %d\n",nxt,p);
            modify(1,0,tot,p,(*pointS[p].begin()));
            modify(1,0,tot,nxt,(*pointS[nxt].begin()));
            it=colorS[t].find(p);
            colorS[t].erase(it);
            if (colorS[t].size()==2) cnt--;
        }
        else
        {
            if (cnt<k) {ans[op[i].t]=-1;continue;}
            int l=0,r=inf;
            while(l<r)
            {
                int mid=(l+r)>>1;
                if (check(op[i].x-mid,op[i].x+mid)) r=mid;
                else l=mid+1;
            }
            ans[op[i].t]=l;
        }
    }
    for(int i=1;i<=q;i++)
        printf("%d\n",ans[i]);
}

int main()
{
    init();
    work();

    return 0;
}
posted @ 2018-06-25 20:12  Maxwei_wzj  阅读(98)  评论(0编辑  收藏  举报