NKOJ 4340 (SCOI 2014)方伯伯的OJ (Splay+map+set)

该博客详细介绍了如何解决SCOI2014比赛中的P4340问题,涉及使用Splay树、map和set的数据结构。博主分析了题目要求,提出了在线处理的策略,通过维护Splay节点的编号和排名,利用map存储编号区间,同时跟踪已拆分的点,从而高效地处理各种操作。给出了具体的解题思路和代码实现。
摘要由CSDN通过智能技术生成

P4340【SCOI2014】方伯伯的Oj

问题描述

这里写图片描述

输入格式

输入的第1行包含2个用空格分隔的整数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 ≤ n ≤ 10^8,1 ≤ m ≤ 10^5

输入保证对于所有的操作 1,2,3,x 必然已经出现在队列中,同时对于所有操作 1,1 ≤ y ≤ 2 × 10^8,并且

y 没有出现在队列中。

对于所有操作 4,保证 1 ≤ k ≤ n。


此题强制在线,观察发现 n 很大,那么显然此题应该根据m入手,考虑将Splay中的点维护一个区间,然后查询到了再拆开成3个点。

需要维护两个值,编号和排名,按照排名建立Splay,用map记录编号区间为 [a,b] 的点,还需要记录当前已经被拆过的点即查找过的位置,这样就可以通过编号找到在Splay中对应的位置,然后还需要开一个map记录表示一个点的点。

然后每种操作找到对应的Splay的位置,拆点后直接处理即可。


代码:

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<set>
#define N 1000005
using namespace std;
struct node
{
    int l,r;
    bool operator<(const node &b)const
    {
        if(l==b.l)return r<b.r;
        return l<b.l;
    }
};
set<int>A;
map<int,int>C;
map<node,int>B;
set<int>::iterator Ai;
map<node,int>::iterator Bi;
int n,m;
int rt,ls[N],rs[N],fa[N],id[N],lid[N],rid[N],v[N],si[N],tot;
void MT(int x)
{si[x]=si[ls[x]]+si[rs[x]]+v[x];}
void Zig(int x)
{
    int y=fa[x],z=fa[y];
    if(z)y==ls[z]?ls[z]=x:rs[z]=x;fa[x]=z;
    ls[y]=rs[x];fa[rs[x]]=y;
    rs[x]=y;fa[y]=x;
    MT(y);MT(x);
}
void Zag(int x)
{
    int y=fa[x],z=fa[y];
    if(z)y==ls[z]?ls[z]=x:rs[z]=x;fa[x]=z;
    rs[y]=ls[x];fa[ls[x]]=y;
    ls[x]=y;fa[y]=x;
    MT(y);MT(x);
}
void Splay(int x)
{
    int y,z;
    while(fa[x])
    {
        y=fa[x],z=fa[y];
        if(z)
        {
            if(y==ls[z])x==ls[y]?(Zig(y),Zig(x)):(Zag(x),Zig(x));
            else x==rs[y]?(Zag(y),Zag(x)):(Zig(x),Zag(x));
        }
        else x==ls[y]?Zig(x):Zag(x);
    }
    rt=x;
}
int Gmax(int p)
{
    while(rs[p])p=rs[p];
    return p;
}
int Gmin(int p)
{
    while(ls[p])p=ls[p];
    return p;
}
int Find(int x)
{
    node tmp;
    if(C.count(x))return C[x];
    Ai=A.lower_bound(x);
    tmp.r=*Ai-1;Ai--;
    tmp.l=*Ai+1;
    Bi=B.lower_bound(tmp);
    return Bi->second;
}
int Div(int p,int x)
{
    int l,r;node tmp;
    Splay(p);
    A.insert(x);
    l=ls[p];r=rs[p];
    fa[l]=fa[r]=ls[p]=rs[p]=0;
    if(lid[p]<x-1)
    {
        tot++;
        lid[tot]=lid[p];
        rid[tot]=x-1;
        v[tot]=x-lid[p];
        fa[l]=tot;ls[tot]=l;
        MT(tot);l=tot;
        tmp.l=lid[l];
        tmp.r=rid[l];
        B[tmp]=l;
    }
    else if(lid[p]==x-1)
    {
        tot++;
        id[tot]=lid[p];
        v[tot]=1;
        fa[l]=tot;
        ls[tot]=l;
        MT(tot);l=tot;
        C[lid[p]]=tot;
    }
    if(x+1<rid[p])
    {
        tot++;
        lid[tot]=x+1;
        rid[tot]=rid[p];
        v[tot]=rid[p]-x;
        fa[r]=tot;rs[tot]=r;
        MT(tot);r=tot;
        tmp.l=lid[r];
        tmp.r=rid[r];
        B[tmp]=r;
    }
    else if(x+1==rid[p])
    {
        tot++;
        id[tot]=x+1;
        v[tot]=1;
        fa[r]=tot;rs[tot]=r;
        MT(tot);r=tot;
        C[x+1]=tot;
    }
    fa[l]=fa[r]=++tot;
    ls[tot]=l;rs[tot]=r;
    id[tot]=x;v[tot]=1;
    C[x]=tot;MT(tot);
    return rt=tot;
}
void Del(int p)
{
    int l=ls[p],r=rs[p];
    fa[l]=fa[r]=ls[p]=rs[p]=0;
    if(!l)rt=r;
    else
    {
        l=Gmax(l);
        Splay(l);
        fa[r]=l;rs[l]=r;
        rt=l;MT(l);
    }
}
int Gkth(int k)
{
    int p=rt;
    while(p)
    {
        if(si[ls[p]]<k&&si[ls[p]]+v[p]>=k)return p;
        if(si[ls[p]]>=k)p=ls[p];
        else k-=si[ls[p]]+v[p],p=rs[p];
    }
    return p;
}
int main()
{
    int i,j,k,x,y,z,p,l,r,t;node tmp;
    scanf("%d%d",&n,&m);int ans=0;
    rt=++tot;lid[rt]=1;rid[rt]=n;v[rt]=si[rt]=n;
    A.insert(0);A.insert(n+1);
    tmp.l=1;tmp.r=n;B[tmp]=rt;
    while(m--)
    {
        scanf("%d",&k);
        if(k==1)
        {
            scanf("%d%d",&x,&y);
            x-=ans;y-=ans;p=Find(x);
            if(v[p]==1)
            {
                id[p]=y;C[y]=p;Splay(p);
                ans=si[ls[p]]+1;
                printf("%d\n",ans);
            }
            else
            {
                p=Div(p,x);id[p]=y;C[y]=p;
                ans=si[ls[p]]+1;
                printf("%d\n",ans);
            }
        }
        if(k==2)
        {
            scanf("%d",&x);
            x-=ans;p=Find(x);
            if(v[p]==1)
            {
                Splay(p);
                ans=si[ls[p]]+1;
                printf("%d\n",ans);
                Del(p);rs[p]=rt;fa[rt]=p;rt=p;
            }
            else
            {
                p=Div(p,x);
                ans=si[ls[p]]+1;
                printf("%d\n",ans);
                Del(p);rs[p]=rt;fa[rt]=p;rt=p;
            }
        }
        if(k==3)
        {
            scanf("%d",&x);
            x-=ans;p=Find(x);
            if(v[p]==1)
            {
                Splay(p);
                ans=si[ls[p]]+1;
                printf("%d\n",ans);
                Del(p);ls[p]=rt;fa[rt]=p;rt=p;
            }
            else
            {
                p=Div(p,x);
                ans=si[ls[p]]+1;
                printf("%d\n",ans);
                Del(p);ls[p]=rt;fa[rt]=p;rt=p;
            }
        }
        if(k==4)
        {
            scanf("%d",&x);
            x-=ans;p=Gkth(x);Splay(p);
            if(v[p]==1)ans=id[p],printf("%d\n",ans);
            else
            {
                t=x-si[ls[p]];
                x=lid[p]+t-1;
                p=Div(p,x);
                ans=id[p];
                printf("%d\n",ans);
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值