NOIP2017列队

Description

  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 想要计算每一次离队事件中,离队的同学的编号是多少。 
  注意:每一个同学的编号不会随着离队事件的发生而改变,在发生离队事件后方阵中同学的编号可能是乱序的。

Input

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

Output

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

Sample Input

2 2 3
1 1
2 2
1 2

Sample Output

1
1
4

HINT

【样例解释】

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

【数据规模】

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

测试点编号 n m q 其他约定
1,2,3,4,5,6 <=1000 <=1000 <=500
7,8,9,10 <=5*104 <=5*104
11,12 =1 <=105 <=105 所有事件x=1
13,14 <=3*105 <=3*105
15,16 <=3*105
17,18 <=105 <=105 <=105

19,20 <=3*105 <=3*105 <=3*105

 
 
 
 
 
 
 
 

思路

  这道题的前三十分暴力似乎很好写,就是模拟,我在赛场上也就写了这三十分,等到前两天学习平衡树后,才知道这道题用平衡树是那么好写,在这里我说一下我的思路。

  看题目描述,我们易知,这道题的难点就是查询和每一行添加学生,我们可以开n+1个平衡树,维护出来当前每一行和最后一列的编号,每一次查询的时候,如果不在最后一列我们就可以在前n个平衡树里寻找,再第x可线段树中找到第y个就是答案,再在最后一列中找到第x个,插入到第x课平衡树里就可以啦,再把答案直接就添加到最后一列的平衡树里就好了,如果查询是在最后一列里面,直接进行最后两行就好了。我们分析一下时间复杂度,O(qlogn)是不是十分优秀,但我们再分析一下空间复杂度n*m,直接懵逼,MLE。

  我们再仔细分析,发现有些同学的左右同学可能这辈子也不变,那我们是不是就可以把他们合成一个节点,记录一下这一个大节点的序号的左端点和右端点是谁,这样我们就可以把空间复杂度降下来了,均摊q。是不是甚好?但是查询就又出现问题了,我们可以根据在当前节点的第几个直接查询到,但删除这同学怎么办?我们是不是可以把这个大节点分裂成三个节点?分裂成答案同学的左半部分,答案同学和答案同学的右半部分,这三部分,删除的时候直接删除答案同学是不是就可以了?但记住原来的大节点也一定要删除!!!这就是这道题的思路。最后还要说一下,Treap最好用结构体,因为常数会比较小,代码会跑的更快。

#include <stdio.h>
#include <stdlib.h>
#define Q 3000001
#define N 300010
#define update(p) treap[p].size=treap[treap[p].lson].size+treap[treap[p].rson].size+treap[p].to-treap[p].from+1
struct Treap
{
int lson,rson,ord,size;
long long from,to;
}treap[Q];
int root[N];
int n,m,q;
int place,idx;
long long ans;
char nc()
{
static char buf[100000],*p1,*p2;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int rd()
{
register int x=0;char s=nc();
while(s<'0'||s>'9')s=nc();
while(s>='0'&&s<='9')x=x*10+s-'0',s=nc();
return x;
}
void lturn(int &p)
{
if(!treap[p].rson) return;
int tmp=treap[p].rson;
treap[p].rson=treap[tmp].lson,treap[tmp].lson=p;
treap[tmp].size=treap[p].size,update(p),p=tmp;
}
void rturn(int &p)
{
if(!treap[p].lson) return;
int tmp=treap[p].lson;
treap[p].lson=treap[tmp].rson,treap[tmp].rson=p;
treap[tmp].size=treap[p].size,update(p),p=tmp;
}
void add(int &p,long long f,long long t)
{
if(!p)
{
p=++idx;
treap[p].from=f,treap[p].to=t;
treap[p].ord=rand(),update(p);
return;
}
add(treap[p].rson,f,t);
update(p);
if(treap[treap[p].rson].ord<treap[p].ord) lturn(p);
if(treap[treap[p].lson].ord<treap[p].ord) rturn(p);
}
void del(int &p,int order)
{
if(!p) return;
if(p==order)
{
if(treap[p].lson*treap[p].rson==0) p=treap[p].lson+treap[p].rson;
else if(treap[treap[p].lson].ord<treap[treap[p].rson].ord) rturn(p);
else if(treap[treap[p].rson].ord<=treap[treap[p].lson].ord) lturn(p);
del(p,order);
return;
}
if(treap[p].lson==order) del(treap[p].lson,order);
if(treap[p].rson==order) del(treap[p].rson,order);
update(p);
}
void find_ans(int &p,int x)
{
if(!p) return;
if(treap[treap[p].lson].size>=x)
{
find_ans(treap[p].lson,x),update(p);
if(treap[treap[p].rson].ord<treap[p].ord) lturn(p);
if(treap[treap[p].lson].ord<treap[p].ord) rturn(p);
return;
}
else if(treap[p].size-treap[treap[p].rson].size<x)
{
find_ans(treap[p].rson,x-treap[p].size+treap[treap[p].rson].size),update(p);
if(treap[treap[p].rson].ord<treap[p].ord) lturn(p);
if(treap[treap[p].lson].ord<treap[p].ord) rturn(p);
return;
}
x-=treap[treap[p].lson].size,ans=treap[p].from+x-1;
if(x==1&&x==treap[p].size-treap[treap[p].lson].size-treap[treap[p].rson].size)
{
del(p,p);
return;
}
if(x==1) treap[p].from++,update(p);
else if(x==treap[p].size-treap[treap[p].lson].size-treap[treap[p].rson].size)
treap[p].to--,update(p);
else
{
int tmp=++idx;
treap[tmp].to=treap[p].to;
treap[p].to=treap[p].from+x-2;
treap[tmp].from=treap[p].from+x;
treap[tmp].rson=treap[p].rson;
treap[p].rson=tmp;
update(tmp),update(p);
treap[tmp].ord=rand();
if(treap[treap[p].rson].ord<treap[p].ord) lturn(p);
if(treap[treap[p].lson].ord<treap[p].ord) rturn(p);
}
}
void find_ans2(int &p,int x)
{
if(!p) return;
if(treap[treap[p].lson].size>=x)
{
find_ans2(treap[p].lson,x),update(p);
if(treap[treap[p].rson].ord<treap[p].ord) lturn(p);
if(treap[treap[p].lson].ord<treap[p].ord) rturn(p);
return;
}
else if(treap[p].size-treap[treap[p].rson].size<x)
{
find_ans2(treap[p].rson,x-treap[p].size+treap[treap[p].rson].size),update(p);
if(treap[treap[p].rson].ord<treap[p].ord) lturn(p);
if(treap[treap[p].lson].ord<treap[p].ord) rturn(p);
return;
}
ans=treap[p].from;
del(p,p);
}
int main()
{
srand(37975);
n=rd();m=rd();q=rd();
for(long long i=1;i<=n;i++)
add(root[i],(i-1)*m+1,(i-1)*m+m-1);
for(long long i=1;i<=n;i++)
add(root[n+1],i*m,i*m);
for(int i=1;i<=q;i++)
{
int a,b;
a=rd();b=rd();
if(b<m) find_ans(root[a],b);
else find_ans2(root[n+1],a);
long long tmp1=ans;
printf("%lld\n",tmp1);
if(b<m)
{
find_ans2(root[n+1],a);
long long tmp2=ans;
add(root[a],tmp2,tmp2);
}
add(root[n+1],tmp1,tmp1);
}
}

  

NOIP2017D2T3 列队—Treap的更多相关文章

  1. 树套树Day2

    滚回来更新,,, 在Day1我们学了最基本的线段树套平衡树 Day2开始我们要学习一些黑科技 (所以很大概率会出现Day3 w 1.线段树上的黑科技 这一段我们分几项来讲 1.权值线段树 权值线段树以 ...

  2. [LuoguP2161[ [SHOI2009]会场预约 (splay)

    题面 传送门:https://www.luogu.org/problemnew/show/P2161 Solution splay 的确有线段树/树状数组的做法,但我做的时候脑残没想到 我们可以考虑写 ...

  3. [NOIP]2017列队——旋转treap/非旋转treap

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

  4. NOIP2017 Day2 T3 列队(treap)

    可以直接用treap上大模拟...n+1个treap维护n行的前m-1个点和最后一列. 需要支持删除一个点或者一段区间,而空间并不支持存下所有的点的时候,可以用一个点代替一个区间,记录区间首项的值和区 ...

  5. 【NOIP2017D2T3】列队

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

  6. 非旋 treap 结构体数组版(无指针)详解,有图有真相

    非旋  $treap$ (FHQ treap)的简单入门 前置技能 建议在掌握普通 treap 以及 左偏堆(也就是可并堆)食用本blog 原理 以随机数维护平衡,使树高期望为logn级别, FHQ  ...

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

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

  8. fhq treap——简单又好写的数据结构

    今天上午学了一下fhq treap感觉真的很好用啊qwq 变量名解释: \(size[i]\)表示以该节点为根的子树大小 \(fix[i]\)表示随机权值 \(val[i]\)表示该节点的值 \(ch ...

  9. Treap基本用法总结

    Treap=Tree+Heap  起名的人非常有才 Treap是啥? 一棵二叉搜索树可能退化成链,那样各种操作的效率都比较低 于是可爱的Treap在每个节点原先值v的基础上加了一个随机数rnd,树的形 ...

随机推荐

  1. 框架学习之Struts2(三)---OGNL和值栈

    一.OGNL概述 1.1OGNL是对象图导航语言(Object-Graph Navigation Languaged)的缩写,他是一种功能强大的表达式语言,通过简单一致的表达式语法,可以存取Java对 ...

  2. 目标检测算法YOLO算法介绍

    YOLO算法(You Only Look Once) 比如你输入图像是100x100,然后在图像上放一个网络,为了方便讲述,此处使用3x3网格,实际实现时会用更精细的网格(如19x19).基本思想是, ...

  3. AWS stolen CPU

    故事的开头是这样的: 一天我正在吃饭,突然就收到了服务器告警(cpu high load),吓的我饭也没吃好,只吃了三碗就回去处理故障了,我在监控上看到了这样子的图: 看见了吧,吃饭那段时间cpu一下 ...

  4. 深入理解.net - 1.继承的本质

    最近偶然看到这个博客你必须知道的.net,作者6的飞起啊,干货十足,还是07年写的...写的也很赞,评论更精彩,在此强烈推荐一波,看的感觉就像沙漠里发现了绿洲一样,很兴奋,意犹未尽,迫不及待的看完一篇 ...

  5. Vue 项目代理设置的优化

    Vue 项目代理设置的优化 Vue 类的项目开发中项目结构基本都是类似于 Vue-cli 生成的方式, 这种方式开发中,最常用到的模式是开启代理进行 mock 调试或远程调试, 也就是使用了 Vue- ...

  6. [LeetCode] Tag Validator 标签验证器

    Given a string representing a code snippet, you need to implement a tag validator to parse the code ...

  7. [LeetCode] Word Abbreviation 单词缩写

    Given an array of n distinct non-empty strings, you need to generate minimal possible abbreviations ...

  8. [LeetCode] Subarray Sum Equals K 子数组和为K

    Given an array of integers and an integer k, you need to find the total number of continuous subarra ...

  9. .NET CORE 2.0 踩坑记录之ConfigurationManager

    在之前.net framework 中使用的ConfigurationManager还是很方便的,不过在.NET CORE 2.0中SDK默认已经不存在ConfigurationManager. 那么 ...

  10. [POI2006]ORK-Ploughing

    Description Byteasar想耕种他那块矩形的田,他每次能耕种矩形的一边(上下左右都行),在他每次耕完后,剩下的田也一定是矩形,每块小区域边长为1,耕地的长宽分别为m和n,不幸的是Byte ...