题解 P4168 【[Violet]蒲公英】
犹豫了很久还是决定来写一篇Pascal题解。
经典的分块入门题。
简单介绍一下分块:
把数据分成一些块,然后预处理。
维护或查询数据时,中间连续的几个块一起操作,边角暴力。
小于两个块的话就直接暴力了。
一般每个块的大小为
当题目强制在线的话用分块,离线就可以用莫队了。
先看题目,
然后就是预处理。这是分块里面很重要的一步。这个想出来以后做题就不难了。
求区间众数需要每个数出现的数量。考虑用前缀和对每一个块维护。
我们还需要维护连续的几个块的众数。
查询的时候直接枚举边角的数就好了(边角暴力)。
详细还是见代码吧:
{P4168}
var
a,d,p,sum,temp:array[1..40000]of longint;//a是原始数组(后来被离散化了),p是离散化前原始的数值,d和temp用来离散化(具体看主程序),sum是查询过程中统计数量的
s:array[0..200,1..40000]of longint;//预处理的前缀和
m:array[1..200,1..200]of longint;//区间众数
n,q,i,x,l,r,num,size,last:longint;
function min(x,y:longint):longint;//不解释
begin
if x<y then
exit(x)
else
exit(y);
end;
procedure swap(var x,y:longint);//不解释
var
t:longint;
begin
t:=x;
x:=y;
y:=t;
end;
procedure quicksort(l,r:longint);//不解释
var
i,j,m,t:longint;
begin
i:=l;
j:=r;
m:=a[(l+r) div 2];
repeat
while a[i]<m do
inc(i);
while a[j]>m do
dec(j);
if i<=j then
begin
t:=a[i];
a[i]:=a[j];
a[j]:=t;
t:=d[i];
d[i]:=d[j];
d[j]:=t;
inc(i);
dec(j);
end;
until i>j;
if l<j then
quicksort(l,j);
if i<r then
quicksort(i,r);
end;
procedure init; //初始化
var
i,j,k:longint;
begin
for i:=1 to num do
begin
for j:=1 to x do
s[i,j]:=s[i-1,j];
for j:=(i-1)*size+1 to min(n,i*size) do//计算前缀和
inc(s[i,a[j]]);
for j:=1 to i do //计算从第j个块到第i个块的区间众数
begin
m[j,i]:=1;
for k:=2 to x do //暴力枚举
if s[i,k]-s[j-1,k]>s[i,m[j,i]]-s[j-1,m[j,i]] then
m[j,i]:=k;
end;
end;
end;
function query(l,r:longint):longint;//查询
var
i,tl,tr:longint;
begin
tl:=(l-1) div size+1; //l所在的块
tr:=(r-1) div size+1; //r所在的块
if tr-tl<=1 then //小于2个块直接暴力
begin
for i:=l to r do
sum[a[i]]:=0;
for i:=l to r do //把所有数的数量都统计出来
inc(sum[a[i]]);
query:=a[l];
for i:=l+1 to r do //暴力枚举
if (sum[a[i]]>sum[query]) or (sum[a[i]]=sum[query]) and (a[i]<query) then
query:=a[i];
end
else
begin
sum[m[tl+1,tr-1]]:=s[tr-1,m[tl+1,tr-1]]-s[tl,m[tl+1,tr-1]];//先把区间众数的数量统计好
for i:=l to min(n,tl*size) do//统计每个边角的数在块内的数量
sum[a[i]]:=s[tr-1,a[i]]-s[tl,a[i]];
for i:=(tr-1)*size+1 to r do
sum[a[i]]:=s[tr-1,a[i]]-s[tl,a[i]];
for i:=l to min(n,tl*size) do//统计边角的数量
inc(sum[a[i]]);
for i:=(tr-1)*size+1 to r do
inc(sum[a[i]]);
query:=m[tl+1,tr-1];
for i:=l to min(n,tl*size) do//暴力枚举
if (sum[a[i]]>sum[query]) or (sum[a[i]]=sum[query]) and (a[i]<query) then
query:=a[i];
for i:=(tr-1)*size+1 to r do
if (sum[a[i]]>sum[query]) or (sum[a[i]]=sum[query]) and (a[i]<query) then
query:=a[i];
end;
query:=p[query]; //注意离散化过,要把答案转回去
end;
begin
read(n,q);
size:=trunc(sqrt(n)); //块的大小
num:=(n-1) div size+1; //一共有几块
for i:=1 to n do
begin
read(a[i]); //读入
d[i]:=i; //把序号记下来,方便离散化
end;
quicksort(1,n); //排序
x:=a[1]; //写的极丑的离散化
a[1]:=1;
p[1]:=x;
for i:=2 to n do
if a[i]=x then
a[i]:=a[i-1]
else
begin
x:=a[i];
a[i]:=a[i-1]+1;
p[a[i]]:=x;
end;
x:=a[n];
temp:=a;
for i:=1 to n do
a[d[i]]:=temp[i];
init;
last:=0;
for i:=1 to q do //查询部分
begin
read(l,r);
l:=(l+last-1) mod n+1; //强制在线
r:=(r+last-1) mod n+1;
if l>r then
swap(l,r);
last:=query(l,r);
writeln(last);
end;
end.