NOIP2017D2T3 列队—Treap
Posted 时间飞逝
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了NOIP2017D2T3 列队—Treap相关的知识,希望对你有一定的参考价值。
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的主要内容,如果未能解决你的问题,请参考以下文章
学术篇NOIP2017 d2t3 列队phalanx splay做法
[luogu P3960] [noip2017 d2t3] 队列