题意:

n<=1e8,m<=1e5.

标程:

 #include<cstdio>
#include<algorithm>
#include<cstring>
#include<map>
using namespace std;
int read()
{
int x=,f=;char ch=getchar();
while (ch<''||ch>'') {if (ch=='-') f=-;ch=getchar();}
while (ch>=''&&ch<='') x=(x<<)+(x<<)+ch-'',ch=getchar();
return x*f;
}
const int N=;
map<int,int> mp;
int sz[N],son[N][],fa[N],L[N],R[N],tot,id[N],n,rt,m,mn,mx,ans,t,rk,x,y,op,g;
void up(int x) {sz[x]=sz[son[x][]]+sz[son[x][]]+R[x]-L[x]+;}
void rot(int &k,int x)
{
int y=fa[x],z=fa[y],l=(son[y][]==x),r=l^;
if (y==k) k=x;else son[z][(son[z][]==y)]=x;
fa[x]=z;fa[y]=x;fa[son[x][r]]=y;
son[y][l]=son[x][r];son[x][r]=y;
up(y);up(x);
}
void spl(int &k,int x)
{
for (int y;x!=k;rot(k,x))
if ((y=fa[x])!=k)
if (son[y][]==x^son[fa[y]][]==y) rot(k,x);else rot(k,y);
}
void ins(int &x,int l,int r,int idx,int f)//在splay的合适位置插入新区间
{
if (!x)
{
L[x=++tot]=l;R[tot]=r;id[tot]=idx;
sz[tot]=r-l+;fa[tot]=f;
return;
}
if (r<L[x]) ins(son[x][],l,r,idx,x);
else ins(son[x][],l,r,idx,x);
up(x);
}
void split(int x,int rk)
{
if (!son[x][]) rt=son[x][],fa[rt]=;//注意换根,保证splay连通
else {
int y=son[x][];
while (son[y][]) y=son[y][];
int z=son[x][];//注意转到根的下一个儿子处,不能转到根
spl(z,y); rt=y;fa[rt]=;
if (son[x][]) son[rt][]=son[x][];fa[son[x][]]=rt;
up(rt);
}
if (L[x]<=rk-) {ins(rt,L[x],rk-,L[x],);spl(rt,tot);}//注意spl前也要判断
if (rk+<=R[x]) {ins(rt,rk+,R[x],rk+,);spl(rt,tot);}
}
int find_rk(int x,int k)//查询rank为k的点(注意rank为k是离散的,不表示排在第k位)
{
if (L[x]<=k&&k<=R[x]) return x;
if (k<L[x]) return find_rk(son[x][],k);
else return find_rk(son[x][],k);
}
int qry_id(int x,int k)//查询rank_k的编号
{
if (k>sz[son[x][]]&&k<=sz[x]-sz[son[x][]])
{
if (L[x]==R[x]) return id[x];
else return L[x]+(k-sz[son[x][]])-;
}
if (k<=sz[son[x][]]) return qry_id(son[x][],k);
else return qry_id(son[x][],k-(sz[x]-sz[son[x][]]));
}
int main()
{
n=read();m=read();
mn=;mx=n;ins(rt,,n,,);//mn从-1往下开始标号,以0来区分是否标记。
while (m--)
{
op=read();x=read()-ans;
if (op==) {printf("%d\n",ans=qry_id(rt,x));continue;}
rk=mp[x];if (!rk) rk=x;
g=find_rk(rt,rk); spl(rt,g);
printf("%d\n",t=sz[son[g][]]+(rk-L[g]+));//离散排名转实际排名
split(g,rk);
if (op==)
{
y=read()-ans;
ins(rt,rk,rk,y,);spl(rt,tot);
mp[y]=rk;mp[x]=;
}else
{
mp[x]=(op==)?--mn:++mx;
ins(rt,mp[x],mp[x],x,);spl(rt,tot);
}
ans=t;
}
return ;
}

易错点:果然又调了很久,不过感觉自己数据分析能力又提高了。。。

1.spl删点换根的时候注意把y旋转到x的右儿子处。如果把y旋转到x处,那么y的右儿子不一定是x,有可能是z,而z的左儿子是x。

2.注意删点函数split中ins之后的splay也要判断是否在区间限制内,反之有可能tot恰好是被删掉的那一个而产生死循环。

3.mn从-1往下开始标号,以0来区分是否标记。

题解:splay+离散排名+区间分裂

一道splay好题。

一开始在纠结怎么实现排名和编号的双转换?

用mp保存每个点的离散排名(就是给不连续参数代替排名)。splay按照实际排名构造,用sz可以查询区间rank_k。对于splay上的每个点保存一段离散排名区间L~R。

排名转编号:排名->sz查询rank_k的点。

编号转排名:编号->离散排名rk->查找该离散排名所在的splay点g->sz[son[g][0]]+rk-L[g]+1.

一般splay是无法保存下所有节点的。

对于动态修改操作,参考noipDay2T3的做法。

一开始只有一个节点。一个节点中保存编号连续的点L~R。如果L!=R,那么这一段的点都没有修改过。执行一个修改操作,就把原来的一个区间删除修改点断开成两个,再插入修改后的点。

对于2和3操作,如果往前面加入的点,离散排名设为--mn,往后加则是++mx。为了节省map空间,只对修改排名的点记录。

这样时空复杂度就只跟操作有关。

也可以建三棵树,往前面加就扔进1树,不变就在2树,往后面加就扔进3树。这样2树中元素太多,可以记录不在2树中的元素(取补)。好像也可以权值线段树。

loj2212 方伯伯的OJ的更多相关文章

  1. BZOJ 3595: [Scoi2014]方伯伯的Oj SBT+可持久化Treap

    3595: [Scoi2014]方伯伯的Oj Time Limit: 6 Sec  Memory Limit: 256 MBSubmit: 102  Solved: 54[Submit][Status ...

  2. 「SCOI2014」方伯伯的 OJ 解题报告

    「SCOI2014」方伯伯的 OJ 和列队有点像,平衡树点分裂维护即可 但是需要额外用个set之类的对编号查找点的位置 插入完了后记得splay,删除时注意特判好多东西 Code: #include ...

  3. 方伯伯的OJ ( onlinejudge )

    方伯伯的OJ 题目描述 方伯伯正在做他的OJ.现在他在处理OJ 上的用户排名问题. OJ 上注册了n 个用户,编号为1 ∼ n,一开始他们按照编号排名.方伯伯会按照心情对这些用户做以下四种操作,修改用 ...

  4. luogu P3285 [SCOI2014]方伯伯的OJ splay 线段树

    LINK:方伯伯的OJ 一道稍有质量的线段树题目.不写LCT splay这辈子是不会单独写的 真的! 喜闻乐见的是 题目迷惑选手 \(op==1\) 查改用户在序列中的位置 题目压根没说位置啊 只有排 ...

  5. 洛谷P3285 [SCOI2014]方伯伯的OJ 动态开点平衡树

    洛谷P3285 [SCOI2014]方伯伯的OJ 动态开点平衡树 题目描述 方伯伯正在做他的 \(Oj\) .现在他在处理 \(Oj\) 上的用户排名问题. \(Oj\) 上注册了 \(n\) 个用户 ...

  6. [SCOI2014]方伯伯的OJ(线段树)

    方伯伯正在做他的Oj.现在他在处理Oj上的用户排名问题.Oj上注册了n个用户,编号为1-n“,一开始他们按照编号排名. 方伯伯会按照心情对这些用户做以下四种操作,修改用户的排名和编号: 1.操作格式为 ...

  7. [SCOI2014]方伯伯的OJ

    看到这道题的第一想法就是要用FHQ treap 过了这道题...于是至今尚未成功(华丽的 T 掉了 (╯‵□′)╯︵┻━┻ ).于是附个地址. 然后水一波博客. 题意简介 emmmm...方伯伯脑抽做 ...

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

    平衡树分裂钛好玩辣! 题目描述 方伯伯正在做他的 OJ.现在他在处理 OJ 上的用户排名问题. OJ 上注册了 \(n\) 个用户,编号为 \(1\sim n\),一开始他们按照编号排名.方伯伯会按照 ...

  9. BZOJ 3595: [Scoi2014]方伯伯的Oj Splay + 动态裂点 + 卡常

    Description 方伯伯正在做他的Oj.现在他在处理Oj上的用户排名问题. Oj上注册了n个用户,编号为1-”,一开始他们按照编号排名.方伯伯会按照心情对这些用户做以下四种操作,修改用户的排名和 ...

随机推荐

  1. Greenplum(PostgreSql)函数实现批量删除表

    项目做库迁移,前期需要经常调整表结构语句,涉及多次的批量drop,本着偷懒精神写了这个函数.鉴于本函数在生产环境有巨大风险,建议测试完毕后立即删除. 主要步骤很简单:1)从pg_tables查询得到相 ...

  2. (转)VS2010-MFC编程入门教程之目录和总结

     目前该教程可以到鸡啄米编程课堂去学习,阅读体验更好,更适合在线学习. 原文目录及链接: 一.VS2010/MFC编程入门教程之目录 第一部分:VS2010/MFC开发环境 VS2010/MFC编程入 ...

  3. 使用Docker创建数据容器

    使用Docker创建数据容器 翻译自: Data-only container madness 1.什么是数据容器? 数据容器就是本身只创建一个volume供其他容器共享,创建完后即退出,不执行任何任 ...

  4. 简单实用的makefile

    简单的makefile 为了说明问题,就新建一组文件如下: 文件布局及运行结果: make clean 按目录归置 文件看起来是是清楚了,但是makefile写得揪心. 实用版 (1)Makefile ...

  5. 洛谷P3959——宝藏

    传送门:QAQQAQ 题意: 参与考古挖掘的小明得到了一份藏宝图,藏宝图上标出了$n$个深埋在地下的宝藏屋, 也给出了这$n$个宝藏屋之间可供开发的$m$条道路和它们的长度. 小明决心亲自前往挖掘所有 ...

  6. flink支持的数据类型讲解(可序列化) 和 内置累加器的运用

    flink支持的数据类型Flink对DataSet和DataStream中可使用的类型加了一些约束.原因是系统可以通过分析这些类型来确定有效的执行策略和选择不同的序列化方式.有7种不同的数据类型:1. ...

  7. Linux下screen的应用

    在linux系统下,通常我们在执行一些运行时间比较长的任务时,放到后台执行或者使用screen和nohup都是不错的选择,因为任务执行的时间太长了,必须等待它执行完毕,在此期间可不能关掉窗口或者断开连 ...

  8. 删除除了特指的某几个文件外的所有文件的Linux指令

    栗子: 不删除 logs文件夹和credential文件夹 1.  rm -rf  !(logs|credential) 2.  ls | grep -v logs |grep -v credenti ...

  9. php抓取远程数据显示在下拉列表中

    前言:周五10月20日的时候,经理让做一个插件,使用的thinkphp做这个demo 使用CURL抓取远程数据时如果出现乱码问题可以加入 header("content-type:text/ ...

  10. 0908CSP-S模拟测试赛后总结

    我早就料到昨天会考两场2333 话说老师终于给模拟赛改名了啊. 距离NOIP祭日还有60天hhh. 以上是废话. %%%DeepinC无敌神 -rank1 zkt神.kx神.动动神 -rank2 有钱 ...