[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.

 

posted @ 2017-11-24 20:08  Philchieh  阅读(646)  评论(0编辑  收藏  举报