zoukankan      html  css  js  c++  java
  • 【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
    ",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
    ",p,pre);
                //printf("erase %d %d
    ",nxt,pre);
                pointS[nxt].insert(p);
                //printf("insert %d %d
    ",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
    ",p,pre);
                //printf("insert %d %d
    ",nxt,pre);
                it=pointS[nxt].find(p);
                pointS[nxt].erase(it);
                //printf("erase %d %d
    ",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
    ",ans[i]);
    }
    
    int main()
    {
        init();
        work();
    
        return 0;
    }
  • 相关阅读:
    二分查找递归和非递归版
    git常用命令记录
    总结下本周所学的建站流程极其经验
    Ubuntu下su命令失败的解决方法,及其环境变量失效解决
    ubuntu下node安装的三种方法
    Markdown学习及如何在博客园中使用
    nmcli使用方法
    Elasticsearch 升级 7.x 版本后,我感觉掉坑里了!
    Spring Data Elasticsearch基本操作自定义查询
    ElasticSearch——聚合
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793309.html
Copyright © 2011-2022 走看看