[jzoj]5478.【NOIP2017提高组正式赛】列队
Link
https://jzoj.net/senior/#main/show/5478
Description
Sylvia 是一个热爱学习的女孩子。
前段时间,Sylvia 参加了学校的军训。众所周知,军训的时候需要站方阵。 Sylvia所在的方阵中有n × m名学生,方阵的行数为 n,列数为 m。
为了便于管理,教官在训练开始时,按照从前到后,从左到右的顺序给方阵中从 1 到 n × m 编上了号码(参见后面的样例)。即:初始时,第 i 行第 j 列的学生的编号是(i − 1) × m + j。
然而在练习方阵的时候,经常会有学生因为各种各样的事情需要离队。在一天中,一共发生了 q 件这样的离队事件。每一次离队事件可以用数对(𝑦,𝑧) (1≤x≤n,1≤y≤m)描述,表示第 x 行第 y 列的学生离队。
在有学生离队后,队伍中出现了一个空位。为了队伍的整齐,教官会依次下达这样的两条指令:
1. 向左看齐。这时第一列保持不动,所有学生向左填补空缺。不难发现在这条指令之后,空位在第 x 行第 m 列。
2. 向前看齐。这时第一行保持不动,所有学生向前填补空缺。不难发现在这条指令之后,空位在第 n 行第 m 列。
教官规定不能有两个或更多学生同时离队。即在前一个离队的学生归队之后,下一个学生才能离队。因此在每一个离队的学生要归队时,队伍中有且仅有第 n 行第 m 列一个空位,这时这个学生会自然地填补到这个位置。
因为站方阵真的很无聊,所以 Sylvia 想要计算每一次离队事件中,离队的同学的编号是多少。
注意:每一个同学的编号不会随着离队事件的发生而改变,在发生离队事件后方阵中同学的编号可能是乱序的。
Solution
题目可以理解为:有一个n*m矩阵顺序编号,每次将第(x,y)坐标的人取走,询问其编号,并将(x,y+1~m)的人向左移,将(x+1~n,m)的人向上移,最终将(x,y)放在(n,m)上。
其实每次操作,无非就是将第x行的其中一个元素删掉,并将第m列的一个元素放置在当前行。同理,上移操作也如此。如图
显然,黄色部分是最终x行的每一个数。
显然,可以用线段树来维护一段区间有多少个数(可能有被取走的空位),来寻找第k个元素,用0或1来存当前位置数的情况。
推荐用线段树二分
考虑到行列特殊性,我们维护每一行的前1~m-1,以及最后一列
每次查询修改即可,考虑动态开点
初始化是要费一番考虑,求答案可以使get[x]表示动态开的第x个点,其代表的数。
Code
{$inline on} uses math; var t,n,m,q,i,x,y,h,pd,tot:longint; len,ans,ans1,num1,num2,ans_w,ans1_w:int64; get:array[0..9000000] of int64; li,ri,num,tree:array[0..9000000] of longint; procedure fyj(x,l,r:longint); inline;//动态开点 var mid:longint; begin mid:=(l+r) shr 1; if li[x]=0 then begin inc(tot); li[x]:=tot; if len>=l then tree[tot]:=min(mid,len)-l+1; if l=mid then if pd=2 then get[tot]:=(h-1)*m+l else get[tot]:=m*l; end; if ri[x]=0 then begin inc(tot); ri[x]:=tot; if len>=mid+1 then tree[tot]:=min(r,len)-mid; if r=mid+1 then if pd=2 then get[tot]:=(h-1)*m+r else get[tot]:=m*r; end; end; procedure change(root,l,r,x,y,z:int64);//表示将第x个数,其线段树值改为y,get值改为z var mid:longint; begin if (l=r) and (x=l) then begin tree[root]:=y; get[root]:=z; exit; end; fyj(root,l,r); mid:=(l+r) shr 1; if x<=mid then change(li[root],l,mid,x,y,z) else change(ri[root],mid+1,r,x,y,z); tree[root]:=tree[li[root]]+tree[ri[root]]; end; procedure find(root,l,r,k:longint);//线段树二分 var mid:longint; begin if l=r then begin num1:=get[root]; num2:=l; exit; end; fyj(root,l,r); mid:=(l+r) shr 1; if tree[li[root]]>=k then find(li[root],l,mid,k) else find(ri[root],mid+1,r,k-tree[li[root]]); tree[root]:=tree[li[root]]+tree[ri[root]]; end; begin assign(input,'phalanx.in');reset(input); assign(output,'phalanx.out');rewrite(output); readln(n,m,q); t:=q; for i:=1 to n do begin num[i]:=m-1; tree[i]:=m-1; end; num[n+1]:=n; tree[n+1]:=n; tot:=n+1; while t>0 do begin dec(t); readln(x,y); if y=m then begin len:=n; pd:=1; find(n+1,1,n+q,x); ans:=num1; ans_w:=num2; writeln(ans); change(n+1,1,n+q,ans_w,0,0); inc(num[n+1]); change(n+1,1,n+q,num[n+1],1,ans); continue; end; len:=n; pd:=1; find(n+1,1,n+q,x); ans:=num1; ans_w:=num2; if ans=0 then ans:=x*m; /////////////////////////////////////////////////// len:=m-1; pd:=2; h:=x; find(x,1,m-1+q,y); ans1:=num1; ans1_w:=num2; writeln(ans1); /////////////////////////////////////////////////// len:=m-1; pd:=2; h:=x; change(x,1,m-1+q,ans1_w,0,0); inc(num[x]); change(x,1,m-1+q,num[x],1,ans); /////////////////////////////////////////////////// len:=n; pd:=1; change(n+1,1,n+q,ans_w,0,0); inc(num[n+1]); change(n+1,1,n+q,num[n+1],1,ans1); end; close(input);close(output); end.