题解 P2709 【小B的询问】
题解 P2709 【小B的询问】
posted on 2018-08-11 20:52:13 | under 题解 | 编辑 | 15题目描述见此处:传送门https://www.luogu.org/problemnew/show/P2709 我交了n遍才过,所以评测记录几乎被我占了1页,求 管理大大不要怪我...... -------------------------------------分割线 解题思路:
此题第一眼:数据结构; 第二眼:暴力O*(n^3); 第三遍(线段树TLE);
最后一眼:莫队似乎可以做哦。
于是我们考虑听y大所说的莫涛队长发明的算法——莫队。
我们还是从暴力写起:
第一遍,无序,让l和r乱跳,时间O(n^2q)爆TLE!
第二遍,排序后莫队,让l有序,r乱跳,时间O(NQ*LG(N)) 还是TLE!
第三遍,考虑分块优化,如果快排时的左端点i和右端点j在同一区间内,即i div sqrt(n)=j div sqrt(n)
那么i,j在同一区间,直接以y数组排序为第一关键字排序即可。
否则以x数组为第一关键字排序。(具体见代码)
我来发个p的题解,也算是为p党做个小贡献。
(现在用p的人真得不多了)
var
n,k,m,l,r,j,i,p:longint;
ans:int64;
a,x,y,c,b,f:array[0..500005] of longint;
procedure sort(l,r:longint);//库里自带的快排
做了些小改动。(这点p真的比不上c++的STL)
var
i,j,xx,yy,yyy:longint;
begin
i:=l;
j:=r;
xx:=x[(l+r) div 2];
yy:=y[(l+r) div 2];//多关键字排序
repeat
if i div p=j div p then//判断i,j是否在同一块内
begin
while (x[i]<xx) do
inc(i);
while (xx<x[j]) do
dec(j);
if not(i>j) then//别忘记交换3个而不是1个数组
begin
yyy:=x[i];
x[i]:=x[j];
x[j]:=yyy;
yyy:=y[i];
y[i]:=y[j];
y[j]:=yyy;
yyy:=c[i];
c[i]:=c[j];
c[j]:=yyy;
inc(i);
j:=j-1;
end;
end else
begin//这部分作用同上
while (y[i]<yy) do
inc(i);
while (yy<y[j]) do
dec(j);
if not(i>j) then
begin
yyy:=x[i];
x[i]:=x[j];
x[j]:=yyy;
yyy:=y[i];
y[i]:=y[j];
y[j]:=yyy;
yyy:=c[i];
c[i]:=c[j];
c[j]:=yyy;
inc(i);
j:=j-1;
end;
end;
until i>j;
if l<j then
sort(l,j);
if i<r then
sort(i,r);
end;
begin
readln(n,m,k);
p:=trunc(sqrt(n));//把序列分成sqrt(n)块
for i:=1 to n do read(a[i]);
for i:=1 to m do
begin
readln(x[i],y[i]);//莫队作为离线算法,当然要保存每个询问啦
c[i]:=i;//c[i]表示当前读入的是第i个数
end;
sort(1,m);
l:=1;
r:=0;
ans:=0;
fillchar(b,sizeof(b),0);
for i:=1 to m do//莫队基本思想(本人喜欢while)
begin
while l>x[i] do begin dec(l); inc(b[a[l]]); ans:=ans+(2*b[a[l]]-1); end;
while r<y[i] do begin inc(r); inc(b[a[r]]); ans:=ans+(2*b[a[r]]-1); end;
while l<x[i] do begin dec(b[a[l]]); ans:=ans-(2*b[a[l]]+1); inc(l); end;
while r>y[i] do begin dec(b[a[r]]); ans:=ans-(2*b[a[r]]+1); dec(r); end;
f[c[i]]:=ans;//用f数组记录读进来时第1-n个答案
end;
for i:=1 to m do writeln(f[i]);
end.
查看9道真题和解析