JZOJ5924. Queue

\[Description \]

Hack 国的居民人人都是 OI 大师,Hometown 得知便赶紧来到 Hack 国学习。可想要进入 Hack 国并不是件容易的事情,首先就必须通过 Hack 国海关小 B 的考验。小 B 觉得 Hometown 比较菜,于是就扔了一道小水题给 Hometown。
给定一个长度为 n 的数列 a i ,接下来会对这个序列进行 m 次操作。操作类型分为以下两种:

\(\texttt{1 l r}\),表示将区间 \(l,r\) 轮转一次。具体来说, \(a_l,a_{l+1}..a_r\) 经过一次轮转之后,会变为 \(a_r,a_l,a_{l+1}..a_{r-1}\)

\(\texttt{1 l r k}\),询问区间 \(l,r\)\(a_i = k\) 的个数。

可惜 Hometown 还是不会做,他只能期待你能解决这个问题了。

\[Input/Output \]

从文件queue.in中读入数据。
第一行两个整数 n,m,表示序列的长度与操作的次数。
第二行 n 个整数 a i ,表示这个序列。
接下来的 m 行,每行先是一个整数 opt 表示操作的类型。对于 opt = 1 的操作,接下来两个整数 l,r 表示将区间 [l,r] 轮转;对于 opt = 2 的操作,接下来三个整数 l,r,k 表示求区间 [l,r] 内等于 k 的值的个数。

输出到文件queue.out中。
对于每个 2 操作,一行一个整数,表示这次询问的答案。

\[Sample \]

7 6
1 2 2 3 2 1 3
2 3 6 2
1 1 6
2 2 4 1
1 3 6
2 6 7 3
2 3 5 2
2
1
2
3

对于第一次询问,区间 [3,6] 中一共出现了 2 次 2。

随后进行修改,修改之后序列变为 1,1,2,2,3,2,3。

对于第二次询问,区间 [2,4] 中一共出现了 1 次 1。

随后再次修改,修改之后序列变为 1,1,2,2,2,3,3。

对于第三次询问,区间 [6,7] 中一共出现了 2 次 3。

对于第四次询问,区间 [3,5] 中一共出现了 3 次 2。

\[Data\ Constraint \]

\(N,M,a_i \leq 10^5\)

这道题异常简单。

考虑异常的修改和普通的查询,我们可以想到分块。但是如何分块可以试一次修改达到 \(\sqrt{N}\) ? 我们要把每一个块的“轮转”变为 \(O(1)\) 的。总的来说,我们的每一次“轮转”都是将块尾给下一个块的块首,然后再将 \(r\) 放到 \(l\) 的位置。这样子的话我们可以开一个队列,然后进行操作。每一个队列试双端队列,数组开 \(-x \times 10^4~y \times 10^4\)。其中看你往正数还是负数倾斜的大,然后确定 \(x\)\(y\) 的大小。空间复杂度可以做到 \(N \sqrt{N}\)

我们轮转的操作包括什么呢?

再一个块中暴力不说。

我们用 \(real_1\)\(real_2\) 分别记录 \(l,r\) 所在的块的编号。
然后我们用 \(goal_1\)\(goal_2\) 分别表示 \(l,r\) 所在的块中的位置。这个位置不是第几个,而是它的下标所在。比如我们用 \(queue_{block,node}\) 代表第 \(block\) 块的 \(node\) 的位置 (并不是第 \(node\) 个因为 \(head_{block}\) 可能是负数),我们 \(goal\) 要求的就是这个 \(node\)。那么很显然:

  • \(goal[1]:=head_{real_1}+l-(real_1-1) \times node_{num}-1\)
  • \(goal[2]:=head_{real_2}+r-(real_2-1) \times node_{num}-1\)

后面是这样子的:

1 3 4 5 6 7
将 2,6 翻转

它的队列是这样的:
1 3
4 5
6 7

那么接下来先把 6 这个位置的数搞进去:

1 6 3
4 5
7

然后把每一个触及到的块都把队尾给(下一个)队头:

1 6
3 4
5 7

搞定

细节很多,要慢慢琢磨。注意我们要定义 \(times_{block,k}\) 代表 \(k\)\(block\) 块的出现次数。查询怎么搞? 同理。

Const
    tot=100100;
    totalmemory=80000000;
    memory=(totalmemory div trunc(sqrt(tot))) div 7;

var
    times,queue:array[-1..trunc(sqrt(tot)*1.3),trunc(-memory*2.5)..trunc(memory/2.5)] of longint;
    head,tail:array[-1..trunc(sqrt(tot)*1.3)] of longint;
    real,goal:array[1..3] of longint;
    i,j,n,m,l,r,l1,r1,k,ans,num,mode,node_num,block_num:longint;

function Locate(node:longint):longint;
begin
    if node mod node_num=0 then exit(node div node_num);
    exit(node div node_num+1);
end;

begin
	assign(input,'queue.in');reset(input);
	assign(output,'queue.out');rewrite(output); 
    read(n,m); if (n=0)or(m=0) then halt; node_num:=trunc(sqrt(n)); block_num:=Locate(n);
    for i:=1 to block_num do head[i]:=1;
    for i:=1 to n do begin read(num); inc(tail[Locate(i)]); queue[Locate(i),tail[Locate(i)]]:=num; inc(times[Locate(i),num]); end;
    for i:=1 to m do
    begin
        read(mode);
        if mode=1 then
        begin
            read(l,r); real[1]:=Locate(l); real[2]:=Locate(r);
			goal[1]:=head[real[1]]+l-(real[1]-1)*node_num-1; goal[2]:=head[real[2]]+r-(real[2]-1)*node_num-1;
			if real[1]=real[2] then
			begin
				r:=queue[real[1],goal[2]];
				for j:=goal[2] downto goal[1]+1 do queue[real[1],j]:=queue[real[1],j-1];
				queue[real[1],goal[1]]:=r;
			end else
			begin
				dec(head[real[1]]); for j:=head[real[1]] to goal[1]-2 do queue[real[1],j]:=queue[real[1],j+1];
				dec(tail[real[2]]); goal[3]:=queue[real[2],goal[2]]; dec(times[real[2],goal[3]]);
				queue[real[1],goal[1]-1]:=goal[3]; inc(times[real[1],goal[3]]);
				for j:=goal[2] to tail[real[2]] do queue[real[2],j]:=queue[real[2],j+1]; queue[real[2],tail[real[2]]+1]:=0;
				for j:=real[1] to real[2]-1 do
				begin
					dec(head[j+1]); queue[j+1,head[j+1]]:=queue[j,tail[j]];
					dec(times[j,queue[j,tail[j]]]); inc(times[j+1,queue[j,tail[j]]]);
					queue[j,tail[j]]:=0; dec(tail[j]);
				end;
			end;
        end else
        begin
            read(l,r,k); real[1]:=Locate(l); real[2]:=Locate(r); ans:=0;
            l1:=l-(real[1]-1)*node_num-1+head[real[1]]; r1:=r-(real[2]-1)*node_num-1+head[real[2]];
			if real[1]=real[2] then
			begin
				for j:=l1 to r1 do if queue[real[1],j]=k then inc(ans);
			end else
			begin
				for j:=l1 to tail[real[1]] do if queue[real[1],j]=k then inc(ans);
				for j:=real[1]+1 to real[2]-1 do inc(ans,times[j,k]);
				for j:=head[real[2]] to r1 do if queue[real[2],j]=k then inc(ans);
			end;
            writeln(ans);
        end;
    end;
	close(input); close(output);
end.
posted @ 2019-01-22 17:17  _ARFA  阅读(126)  评论(0编辑  收藏  举报