[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.
时间: 2024-08-30 13:25:04

[jzoj]5478.【NOIP2017提高组正式赛】列队的相关文章

计蒜客NOIP2017提高组模拟赛(五)day1-展览

传送门 发现这题选或不选对状态的优劣程度不会产生影响,如果已经确定了两个数a和b,那么最优的首项和公比也都是唯一确定的, 与对于后面的数x,加进去也好不加进去也好,首项和公比依旧是原来的 于是我们用尺取算法,用两个指针来扫一遍, 如果只有一个数且下一个数能被整除,就加进去,然后确定首项和公比 如果只有一个数且下一个数不能整除,两个指针直接指向下一个数 如果有多个数且下一个数满足公式,就加进来 如果有多个数且下一个数不满足公式,两个指针直接指向下一个数 这样对于最优解,一定是可以找到的 顺便说下最

ZROI提高组模拟赛05总结

ZROI提高组模拟赛05总结 感觉是目前为止最简单的模拟赛了吧 但是依旧不尽人意... T1 有一半的人在30min前就A掉了 而我花了1h11min 就是一个简单的背包,我硬是转化了模型想了好久,生生把一个弱智题变成了一个不可做题 最后竟然在转化两次后的模型上以为自己做出来了 这个题比别人多花的1h左右的时间,而且只得到了30分,成为了这场比赛失败的关键因素 T2 依旧是一道简单题 有人20min之内就A掉了 感觉放在CF里最多算一道Div2 D,还是简单的那种 可是我又一次想复杂了 大意就是

提高组模拟赛总结(1)

T1 : 题意:给定一个连续的颜色序列,至多可以去掉k种颜色,问能得到的最大连续单个颜色长度是多少 n <= 10 ^ 5 做法:考试的时候十分斯波,在统计的时候写了线段树统计,实际上根本不用 维护了两个指针表示当前的序列,离散化后用桶维护每一个颜色的数量,如果颜色 <= k + 1就一直加颜色进来,不然就一直移动左指针删除颜色,每次改变某一颜色数量时更新一下最大值就好了 T2: 题意:(实在没办法总结一句话题意了ORZ,总之是个变种的Lis)有 n 个节目,其描述了在 Ti时 刻 Xi号社团

[SinGuLaRiTy] NOIP2017 提高组

[SinGuLaRiTy-1048] Copyright (c) SinGuLaRiTy 2018. All Rights Reserved. NOIP2017过了这么久,现在2018了才找到寒假这么一个空挡来写题解.哎,没办法,谁叫学校抓的紧呢. 序 | Before 这是我的最后一次NOIP. 因为是最后一次的原因吧,考前压力就蛮大的,再加上各种模拟赛,模板练习的轮番轰炸,走进考场时整个人都是“飘飘欲仙”的感觉~ 我的NOIP2017就在这种“飘飘欲仙”的氛围下开始了. 游记 | Blogs

[随笔]NOIP2017提高组复赛 游记

前言 真正意义上的第一篇游记,经历了第一次正式大考,希望自己能在这条道路上走得足够远... 当然最主要的梦想还是THU喔! Day -2 今天就是NOIP考前在机房呆的最后一天,上午打完了最后一场模拟赛,是NOIP模拟赛50,难以置信集训期间考了这么多场QAQ 应该这场就是信心赛了,因为好像上午要和我们联考的学校放了OD鸽子了2333(喜闻乐见) 然后蒟蒻的我信心赛只打了210分rank12 T-T,只能说RP++ 下午没有考试,于是机房内的气氛从原来的高级算法乱飞,变成了都在码最基础的模板 临

2014-10-18 noip提高组模拟赛(codecomb)[未填]

> <看了一下觉得挺难的...除了T2 T1只想到了找环,> <倍增的思想没有学过,所以看题解看得雨里雾里的(最近真的打算学一下!) T2贪心..很容易看出的 T3感觉题目没有怎么看懂...> <正解居然是树形dp 果然不太会 T4蒟蒻> <我连最小子矩阵都不会求T_T其他更不用说了 虽然没有参加比赛,但感觉自己到不了200(hzwer说没有200应该去参加普及组QAQ) 题目出的挺好的,觉得noip极有可能出现T1T2T3,所以在此mark 而且T1以为是

提高组模拟赛总结(3)

貌似是tyvj 2012年的题? T1:给出一个有向无环图,起点为1终点为N,每条边都有一个长度,并且从起点出发能够到达所有的点,所有的点也都能够到达终点.绿豆蛙从起点出发,走向终点. 到达每一个顶点时,如果有K条离开该点的道路,绿豆蛙可以选择任意一条道路离开该点,并且走向每条路的概率为 1/K . 现在绿豆蛙想知道,从起点走到终点的所经过的路径总长度期望是多少? 刚看到题目,歪歪斜斜的每页上都写着'水题'几个字.我横竖睡不着,仔细看了半夜,才从字缝里看出字来,满本都写着一个字是'坑'!" 显然

NOIP2017提高组-滚粗记

这次是真的滚粗了 感觉自己被欺骗了 本来还以为自己一等应该可以的,但是如今事实就这么摆着,想哭也没有用了 DAY -1:在机房里整理了些模板,担心这个要考又担心那个要考 DAY 0:早上又看了会儿模板,中午就出发了,差不多晚上5点多到的帝京(酒店好像还不错噢),过了一会儿就出发去衢二中领证吃饭了,晚上回来就在房间里面颓废,睡得也挺晚的 DAY 1:早上6点被闹钟叫醒,8:30考试,还有点小紧张,T1看了好久一直不会,找了几个样例后发现好像有规律但是又找不到,挺急的!后来不知道怎么就感觉找到规律了

提高组模拟赛总结(2)

T1: 题意:给定一个01背包,求将背包装到不能再放任何剩余物品的方案数 做法:部分分 F[j][k]表示前i个物品分配j空间,最小没有使用的物品为k的方案数 F[j][k] = Max(F[j-w[i]][k] + a[i], F[j][k]) 实际上并不需要枚举k,显然物品越重其前面必选的物品就越多,能提供的决策就越少,考虑对物品从大到小排序,此时如果取了第i个物品,i+1 .. n必然要被选择到,如果此时剩下的钱刚好放不下i且放得下 i - 1,那就说明 i 就是没有使用的最小的物品,可以