洛谷 P3285 / loj 2212 [SCOI2014] 方伯伯的 OJ 题解【平衡树】【线段树】

作者: wjyyy 分类: Treap,线段树,解题报告 发布时间: 2019-03-08 11:25

点击量:26

平衡树分裂钛好玩辣!

题目描述

方伯伯正在做他的 OJ。现在他在处理 OJ 上的用户排名问题。

OJ 上注册了 \(n\) 个用户,编号为 \(1\sim n\),一开始他们按照编号排名。方伯伯会按照心情对这些用户做以下四种操作,修改用户的排名和编号:

  1. 操作格式为 1 x y,意味着将编号为 \(x\) 的用户编号改为 \(y\),而排名不变,执行完该操作后需要输出该用户在排名中的位置,数据保证 \(x\) 必然出现在排名中,同时 \(y\) 是一个当前不在排名中的编号。
  2. 操作格式为 2 x,意味着将编号为 \(x\) 的用户的排名提升到第一位,执行完该操作后需要输出执行该操作前编号为 \(x\) 用户的排名。
  3. 操作格式为 3 x,意味着将编号为 \(x\) 的用户的排名降到最后一位,执行完该操作后需要输出执行该操作前编号为 \(x\) 用户的排名。
  4. 操作格式为 4 k,意味着查询当前排名为 \(k\) 的用户编号,执行完该操作后需要输出当前操作用户的编号。 但同时为了防止别人监听自己的工作,方伯伯对他的操作进行了加密,即将四种操作的格式分别改为了:
1 x+a y+a
2 x+a
3 x+a
4 k+a

其中 \(a\) 为上一次操作得到的输出,一开始 \(a=0\)。

例如: 上一次操作得到的输出是 5
这一次操作的输入为:1 13 15
因为这个输入是经过加密后的,所以你应该处理的操作是1 8 10

现在你截获了方伯伯的所有操作,希望你能给出结果。

输入输出格式

输入格式:

输入的第一行包含两个用空格分隔的整数 \(n\) 和 \(m\),表示初始用户数和操作数。

此后有 \(m\) 行,每行是一个询问,询问格式如上所示。

输出格式:

输出包含 \(m\) 行。每行包含一个整数,其中第 \(i\) 行的整数表示第 \(i\) 个操作的输出。

输入输出样例

输入样例:

10 10
1 2 11
3 13
2 5
3 7
2 8
2 10
2 11
3 14
2 18
4 9

输出样例:

2
2
2
4
3
5
5
7
8
11

数据范围与约定

对于 \(100\%\) 的数据,\(1\le n\le 10^8,\ 1\le m\le 10^5\)。

输入保证对于所有的操作,\(x\) 必然已经出现在队列中,同时对于所有操作 1,\(1\le y\le 2\times 10^8\),并且 \(y\) 没有出现在队列中。

对于所有操作 4,保证 \(1\le k\le n\)。

题解:

平衡树”区间移动“题。

用带点分裂的 fhq Treap 移动区间,用线段树维护区间内元素在平衡树上的数组下标。

在平衡树上保留父亲信息。其中fa[root]=0 ,然后就可以顺着爬上去来计算每个节点的排名。

时间复杂度 \(O(m\log n)\)。

Code:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#define len(x) (r[x]-l[x]+1)
#define mid ((L+R)>>1)
#define N 200000000

namespace sgt
{
int ls[6000000],rs[6000000],v[6000000],lazy[6000000],pcnt=1;
void pushdown(int k,int L,int R)
{
    if(!lazy[k]||L==R)
        return;
    int x=lazy[k];
    v[ls[k]]=x;
    lazy[ls[k]]=x;
    v[rs[k]]=x;
    lazy[rs[k]]=x;
    lazy[k]=0;
    return;
}
void change(int k,int l,int r,int L,int R,int x)//把区间[l,r]改成x
{
    if(l<=L&&r>=R)
    {
        v[k]=x;
        lazy[k]=x;
        return;
    }
    if(r<L||l>R)
        return;
    if(!ls[k])
        ls[k]=++pcnt;
    if(!rs[k])
        rs[k]=++pcnt;
    pushdown(k,L,R);
    change(ls[k],l,r,L,mid,x);
    change(rs[k],l,r,mid+1,R,x);
}
int ask(int k,int p,int L,int R)//求点i的编号
{
    if(!ls[k]&&!rs[k])
        return v[k];
    pushdown(k,L,R);
    if(p<=mid)
        return ask(ls[k],p,L,mid);
    return ask(rs[k],p,mid+1,R);
}
}
int root=0;

namespace bst
{
int rdm[200100],ls[200100],rs[200100],fa[200100];
int sz[200100],l[200100],r[200100];
int pcnt=0;
int Newnode(int L,int R)
{
    ++pcnt;
    l[pcnt]=L;
    r[pcnt]=R;
    sz[pcnt]=R-L+1;
    rdm[pcnt]=rand();
    sgt::change(1,L,R,1,N,pcnt);
    return pcnt;
}
void maintain(int k)
{
    sz[k]=sz[ls[k]]+sz[rs[k]]+r[k]-l[k]+1;
    fa[ls[k]]=k;
    fa[rs[k]]=k;
}
int Merge(int a,int b)
{
    if(!a||!b)
        return a|b;
    if(rdm[a]<rdm[b])
    {
        rs[a]=Merge(rs[a],b);
        maintain(a);
        return a;
    }
    ls[b]=Merge(a,ls[b]);
    maintain(b);
    return b;
}
void split(int rt,int x,int &a,int &b)
{
    if(!rt)
    {
        a=0,b=0;
        return;
    }
    int L=sz[ls[rt]],R=r[rt]-l[rt]+1;
    if(x<=L)
    {
        b=rt;
        split(ls[rt],x,a,ls[b]);
    }
    else if(x<L+R)
    {
        x-=L;
        a=rt;
        b=Newnode(l[rt]+x,r[rt]);
        r[a]=l[rt]+x-1;

        rs[b]=rs[a];
        rs[a]=0;
        maintain(a);
        maintain(b);
        return;
    }
    else
    {
        a=rt;
        split(rs[rt],x-L-R,rs[a],b);
    }
    maintain(rt);
}
int Rank(int p)
{
    int k=sgt::ask(1,p,1,N);

    int ans=sz[ls[k]]+p-l[k]+1;

    while(fa[k])
    {
        if(k==rs[fa[k]])
            ans+=sz[ls[fa[k]]]+len(fa[k]);
        k=fa[k];
    }
    return ans;
}
}

int main()
{
    int n,m,op,x,y,z=0;
    int a,b,c;
    scanf("%d%d",&n,&m);
    root=bst::Newnode(1,n);
    while(m--)
    {
        scanf("%d",&op);
        if(op==1)
        {
            scanf("%d%d",&x,&y);
            x-=z,y-=z;
            z=bst::Rank(x);

            printf("%d\n",z);

            bst::split(root,z-1,a,b);
            bst::split(b,1,b,c);

            bst::l[b]=bst::r[b]=y;
            sgt::change(1,y,y,1,N,b);

            root=bst::Merge(bst::Merge(a,b),c);
            bst::fa[root]=0;
        }
        else if(op==2||op==3)
        {
            scanf("%d",&x);
            x-=z;
            z=bst::Rank(x);

            printf("%d\n",z);

            bst::split(root,z-1,a,b);
            bst::split(b,1,b,c);

            if(op==2)
                root=bst::Merge(bst::Merge(b,a),c);
            else
                root=bst::Merge(bst::Merge(a,c),b);
            bst::fa[root]=0;
        }
        else
        {
            scanf("%d",&x);
            x-=z;
            bst::split(root,x-1,a,b);
            bst::split(b,1,b,c);
            z=bst::l[b];
            printf("%d\n",z);
            root=bst::Merge(bst::Merge(a,b),c);
            bst::fa[root]=0;
        }
    }
    return 0;
}

说点什么

avatar
  Subscribe  
提醒
/* */