Sylvia 是一个热爱学习的女孩子。 
  前段时间,Sylvia 参加了学校的军训。众所周知,军训的时候需要站方阵。 Sylvia所在的方阵中有n × m名学生,方阵的行数为 n,列数为m。 
  为了便于管理,教官在训练开始时,按照从前到后,从左到右的顺序给方阵中的学生从 1 到 n × m 编上了号码(参见后面的样例)。即:初始时,第 i 行第 j 列的学生的编号是(i − 1) × m + j。 
  然而在练习方阵的时候,经常会有学生因为各种各样的事情需要离队。在一天中,一共发生了 q 件这样的离队事件。每一次离队事件可以用数对(x, y) (1≤x≤n, 1≤y≤m)描述,表示第 x 行第 y 列的学生离队。 
  在有学生离队后,队伍中出现了一个空位。为了队伍的整齐,教官会依次下达这样的两条指令: 
    1. 向左看齐。这时第一列保持不动,所有学生向左填补空缺。不难发现在这条指令之后,空位在第 x 行第 m 列。 
    2. 向前看齐。这时第一行保持不动,所有学生向前填补空缺。不难发现在这条指令之后,空位在第 n 行第 m 列。 
  教官规定不能有两个或更多学生同时离队。即在前一个离队的学生归队之后,下一个学生才能离队。因此在每一个离队的学生要归队时,队伍中有且仅有第 n 行第 m 列一个空位,这时这个学生会自然地填补到这个位置。 
  因为站方阵真的很无聊,所以 Sylvia 想要计算每一次离队事件中,离队的同学的编号是多少。 
  注意:每一个同学的编号不会随着离队事件的发生而改变,在发生离队事件后方阵中同学的编号可能是乱序的。

输入共 q+1 行。 
第1 行包含 3 个用空格分隔的正整数 n, m, q,表示方阵大小是 n行 m 列,一共发生了q 次事件。 
接下来 q 行按照事件发生顺序描述了 q 件事件。每一行是两个整数 x, y,用一个空格分隔,表示这个离队事件中离队的学生当时排在第 x 行第 y 列。

按照事件输入的顺序,每一个事件输出一行一个整数,表示这个离队事件中离队学生的编号。

样例输入

2 2 3 1 1 2 2 1 2

样例输出

1 1 4

【样例解释】

列队的过程如上图所示,每一行描述了一个事件。 
在第一个事件中,编号为 1 的同学离队,这时空位在第一行第一列。接着所有同学向左标齐,这时编号为 2 的同学向左移动一步,空位移动到第一行第二列。然后所有同学向上标齐,这时编号为 4 的同学向上一步,这时空位移动到第二行第二列。最后编号为1 的同学返回填补到空位中。

【数据规模】

数据保证每一个事件满足 1≤x≤n,1≤y≤m。

这道题有许多做法,树状数组、线段树、平衡树都能做。这里只讲一下平衡树treap的做法。

通过题意我们会发现每次操作只改变第x行和最后一列。所以我们可以建n+1个平衡树(每行前m-1个数是一个,最后一列是一个),这样就可以快速查找并删除要操作的点并在最后一列插入删除的点。时间复杂度是O(Q*logN)级别,但看一下数据范围n*m的矩阵9*1010(比全世界人口都要多qwq),显然是存不下的。但只有3*105次查询,所以有许多点是不会被改动的,那么我们可以把这些点缩成一个点,然后记录一下每个点的左端和长度。每次操作时把操作的数所在点分成三个点(l~x-1;x;x+1~r)。所以最多只有2*q+2*n个点。每个平衡树在初始时只有一个点(最后一列的那个树是n个点)。注意非旋转treap分裂时并不是像常规一样分裂成两棵树,而是分裂成两个数和查询点三部分。

旋转treap

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int a,b;
int tot;
int n,m,q;
long long ans;
int w[3000010];
int ls[3000010];
int rs[3000010];
int root[300010];
int size[3000010];
long long l[3000010];
long long r[3000010];
void updata(int x)
{
size[x]=size[ls[x]]+size[rs[x]]+r[x]-l[x]+1;
}
void lturn(int &x)
{
if(!rs[x])
{
return ;
}
int t=rs[x];
rs[x]=ls[t];
ls[t]=x;
size[t]=size[x];
updata(x);
x=t;
}
void rturn(int &x)
{
if(!ls[x])
{
return ;
}
int t=ls[x];
ls[x]=rs[t];
rs[t]=x;
size[t]=size[x];
updata(x);
x=t;
}
void rotate(int &x)
{
if(w[rs[x]]<w[x])
{
lturn(x);
}
if(w[ls[x]]<w[x])
{
rturn(x);
}
}
void insert(int &x,long long L,long long R)
{
if(!x)
{
x=++tot;
l[x]=L;
r[x]=R;
w[x]=rand();
updata(x);
return ;
}
insert(rs[x],L,R);
updata(x);
rotate(x);
}
void del(int &x,int num)
{
if(!x)
{
return ;
}
if(x==num)
{
if(ls[x]*rs[x]==0)
{
x=ls[x]+rs[x];
}
else if(w[ls[x]]<w[rs[x]])
{
rturn(x);
}
else if(w[ls[x]]>=w[rs[x]])
{
lturn(x);
}
del(x,num);
return ;
}
if(ls[x]==num)
{
del(ls[x],num);
}
if(rs[x]==num)
{
del(rs[x],num);
}
updata(x);
}
void find1(int &x,int v)
{
if(!x)
{
return ;
}
if(size[ls[x]]>=v)
{
find1(ls[x],v);
updata(x);
rotate(x);
return ;
}
else if(size[x]-size[rs[x]]<v)
{
find1(rs[x],v-size[x]+size[rs[x]]);
updata(x);
rotate(x);
return ;
}
v-=size[ls[x]];
ans=l[x]+v-1;
if(v==1&&v==size[x]-size[rs[x]]-size[ls[x]])
{
del(x,x);
return ;
}
if(v==1)
{
l[x]++;
updata(x);
}
else if(v==size[x]-size[ls[x]]-size[rs[x]])
{
r[x]--;
updata(x);
}
else
{
int sum=++tot;
r[sum]=r[x];
r[x]=l[x]+v-2;
l[sum]=l[x]+v;
rs[sum]=rs[x];
rs[x]=sum;
updata(sum);
updata(x);
w[sum]=rand();
rotate(x);
}
}
void find2(int &x,int v)
{
if(!x)
{
return ;
}
if(size[ls[x]]>=v)
{
find2(ls[x],v);
updata(x);
rotate(x);
return ;
}
else if(size[x]-size[rs[x]]<v)
{
find2(rs[x],v-size[x]+size[rs[x]]);
updata(x);
rotate(x);
return ;
}
ans=l[x];
del(x,x);
}
int main()
{
srand(37975);
scanf("%d%d%d",&n,&m,&q);
for(long long i=1;i<=n;i++)
{
insert(root[i],(i-1)*m+1,(i-1)*m+m-1);
}
for(long long i=1;i<=n;i++)
{
insert(root[n+1],i*m,i*m);
}
while(q--)
{
scanf("%d%d",&a,&b);
if(b<m)
{
find1(root[a],b);
}
else
{
find2(root[n+1],a);
}
long long s=ans;
printf("%lld\n",s);
if(b<m)
{
find2(root[n+1],a);
long long t=ans;
insert(root[a],t,t);
}
insert(root[n+1],s,s);
}
return 0;
}

非旋转treap

#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<cstdio>
#include<vector>
#include<bitset>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
#define pr pair<int,ll>
using namespace std;
int rs[2000010];
int ls[2000010];
ll l[2000010];
ll r[2000010];
int size[2000010];
int num[2000010];
int root[500010];
int v[2000010];
int n,m,q;
int x,y;
int cnt;
int a,b,c,d;
pr now;
int build(ll L,ll R)
{
int rt=++cnt;
size[rt]=num[rt]=(int)(R-L+1);
l[rt]=L;
r[rt]=R;
v[rt]=rand();
return rt;
}
void pushup(int rt)
{
size[rt]=size[ls[rt]]+size[rs[rt]]+num[rt];
}
pr split(int rt,int k,int &x,int &y)
{
if(size[ls[rt]]>=k)
{
y=rt;
pr res=split(ls[rt],k,x,ls[y]);
pushup(rt);
return res;
}
else if(size[ls[rt]]+num[rt]>=k)
{
x=ls[rt];
y=rs[rt];
return make_pair(rt,1ll*(k-size[ls[rt]]+l[rt]-1));
}
else
{
x=rt;
pr res=split(rs[rt],k-size[ls[rt]]-num[rt],rs[x],y);
pushup(rt);
return res;
}
}
int merge(int x,int y)
{
if(!x||!y)
{
return x+y;
}
if(v[x]<v[y])
{
rs[x]=merge(rs[x],y);
pushup(x);
return x;
}
else
{
ls[y]=merge(x,ls[y]);
pushup(y);
return y;
}
}
int main()
{
srand(12378);
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=n;i++)
{
root[i]=build(1ll*(i-1)*m+1,1ll*i*m-1);
}
root[n+1]=build(1ll*m,1ll*m);
for(int i=2;i<=n;i++)
{
root[n+1]=merge(root[n+1],build(1ll*i*m,1ll*i*m));
}
while(q--)
{
scanf("%d%d",&x,&y);
if(y==m)
{
now=split(root[n+1],x,a,b);
ls[now.first]=rs[now.first]=0;
size[now.first]=num[now.first];
printf("%lld\n",now.second);
root[n+1]=merge(a,merge(b,now.first));
}
else
{
now=split(root[x],y,a,b);
printf("%lld\n",now.second);
if(l[now.first]!=now.second)
{
cnt++;
l[cnt]=l[now.first];
r[cnt]=now.second-1;
size[cnt]=num[cnt]=(int)(r[cnt]-l[cnt]+1);
v[cnt]=rand();
a=merge(a,cnt);
}
if(r[now.first]!=now.second)
{
cnt++;
l[cnt]=now.second+1;
r[cnt]=r[now.first];
size[cnt]=num[cnt]=(int)(r[cnt]-l[cnt]+1);
v[cnt]=rand();
b=merge(cnt,b);
}
root[x]=merge(a,b);
l[now.first]=now.second;
r[now.first]=now.second;
num[now.first]=size[now.first]=1;
ls[now.first]=rs[now.first]=0;
root[n+1]=merge(root[n+1],now.first);
now=split(root[n+1],x,c,d);
ls[now.first]=rs[now.first]=0;
size[now.first]=num[now.first];
root[n+1]=merge(c,d);
root[x]=merge(root[x],now.first);
}
}
}

[NOIP]2017列队——旋转treap/非旋转treap的更多相关文章

  1. 平衡树及笛卡尔树讲解(旋转treap,非旋转treap,splay,替罪羊树及可持久化)

    在刷了许多道平衡树的题之后,对平衡树有了较为深入的理解,在这里和大家分享一下,希望对大家学习平衡树能有帮助. 平衡树有好多种,比如treap,splay,红黑树,STL中的set.在这里只介绍几种常用 ...

  2. NOIP 2017 列队 - Splay - 树状数组

    题目传送门 传送点I 传送点II 题目大意 (家喻户晓的题目应该不需要大意) (我之前咋把NOIP 2017打成了NOIP 2018,好绝望) Solution 1 Splay 每行一颗Splay,没 ...

  3. BZOJ4864[BeiJing 2017 Wc]神秘物质——非旋转treap

    题目描述 21ZZ 年,冬. 小诚退休以后, 不知为何重新燃起了对物理学的兴趣. 他从研究所借了些实验仪器,整天研究各种微观粒子.这 一天, 小诚刚从研究所得到了一块奇异的陨石样本, 便迫不及待地开始 ...

  4. [NOIp 2017]列队

    Description Sylvia 是一个热爱学习的女孩子. 前段时间,Sylvia 参加了学校的军训.众所周知,军训的时候需要站方阵. Sylvia 所在的方阵中有$n \times m$名学生, ...

  5. 洛谷 P3960 [ NOIP 2017 ] 列队 —— 线段树

    题目:https://www.luogu.org/problemnew/show/P3960 NOIP 题,不用很复杂的数据结构...但又参考了许多: 要求支持维护删除第 k 个和在末尾插入的数据结构 ...

  6. 左偏树 / 非旋转treap学习笔记

    背景 非旋转treap真的好久没有用过了... 左偏树由于之前学的时候没有写学习笔记, 学得也并不牢固. 所以打算写这么一篇学习笔记, 讲讲左偏树和非旋转treap. 左偏树 定义 左偏树(Lefti ...

  7. 旋转/非旋转treap的简单操作

    treap(树堆) 是在二叉搜索树的基础上,通过维护随机附加域,使其满足堆性质,从而使树相对平衡的二叉树: 为什么可以这样呢? 因为在维护堆的时候可以同时保证搜索树的性质: (比如当一棵树的一个域满足 ...

  8. [bzoj3173]最长上升子序列_非旋转Treap

    最长上升子序列 bzoj-3173 题目大意:有1-n,n个数,第i次操作是将i加入到原有序列中制定的位置,后查询当前序列中最长上升子序列长度. 注释:1<=n<=10,000,开始序列为 ...

  9. 关于非旋转treap的学习

    非旋转treap的操作基于split和merge操作,其余操作和普通平衡树一样,复杂度保证方式与旋转treap差不多,都是基于一个随机的参数,这样构出的树树高为\(logn\) split 作用:将原 ...

随机推荐

  1. mybatis抽取出的工具-(一)通用标记解析器(即拿即用)

    目录 1. 简介 1.1 mybatis-config.xml 中使用 1.2 xxxMapper.xml 中使用 2. 原理 2.1 GenericTokenParser 成员变量 2.2 Gene ...

  2. 面试 16:栈的压入压出队列(剑指 Offer 第 22 题)

    我们今天继续来看看周五留下的习题: 面试题:输入两个整数序列,第一个序列表示栈的压入顺序,请判断二个序列是否为该栈的弹出顺序.假设压入栈的所有数字均不相等.例如:压入序列为{1,2,3,4,5},那{ ...

  3. 2019年第一天——使用Visual Studio 2019 Preview创建第一个ASP.Net Core3.0的App

    一.前言: 全文翻译自:https://www.talkingdotnet.com/creating-first-asp-net-core-3-0-app-visual-studio-2019/ Vi ...

  4. 我的微信小程序第三篇(app.json)

    前言 端午节回家了,所以好多天没有更新,只想说还是待在家里舒服呀,妈妈各种做好吃的,小侄子侄女各种粘着我在室外玩,导致我三天下来不仅胖了一圈,还黑了一圈,上班第一天有同事就说我晒黑了,哭~~~,为了防 ...

  5. A4纸尺寸 web打印报告

    A4纸对应的像素尺寸: <style> @media print { .Noprn{ display:none;} .print-hidden { display: none !impor ...

  6. c++入门之初话结构体

    结构体是一种具有一定数据结构思想的数据类型,我们在对待结构体的时候,用该从数据结构的思想去审视结构体.下面给出结构体的定义 struct mystruct {]; int score; double ...

  7. Vladik and Favorite Game CodeForces - 811D (思维+BFS+模拟+交互题)

    D. Vladik and Favorite Game time limit per test 2 seconds memory limit per test 256 megabytes input ...

  8. scrapy之日志等级

    scrapy之日志等级 在settings.py中配置如下项: LOG_LEVEL = 'ERROR' # 当LOG_LEVEL设置为ERROR时,在进行日志打印时,只是打印ERROR级别的日志 这样 ...

  9. Java对象的创建、内存布局和访问定位

    在Java运行时数据区中,我们知道了虚拟机内存的概况,本文介绍虚拟机内存中的数据的其它细节,如对象如何创建.如何布局以及如何访问. 基于实用的原则,这里以HotSpot虚拟机和常用的内存区域Java堆 ...

  10. Excel之批量改变特定字体颜色(转载)

    改变单元格内部分特定字符的颜色,如果批量操作,需要用宏处理, 如下例,将范围内 所有字母A 变成红色 操作步骤:右键点击工作表标签,查看代码,如下代码复制进去Private Sub CommandBu ...