[noip2011模拟赛]区间问题
有N个数,Q个操作(N<=1000000,Q<=3000)。
操作为两种:1、L-R区间中所有数加一个W;2、询问L-R区间中所有数>=C的个数。(每次操作给出W或C)
本题由于询问操作,我们直接用线段树或伸展树无法在区间中直接找出>=某个数的所有数来。
一种解决的方法是:对于原序列建一颗splay,询问时找出区间后,对区间所在子树从子树根开始进行搜索,若当前子树所有节点的最小值>=C,则加上子树大小;否则递归调用左右子树。
对于单次操作,修改是O(logn)的,但询问最坏情况就是O(n)的,所以总复杂度为O(NQ),与暴力无二异。
我们也可以尝试用树套树,但由于修改也是在区间上,所以也很难下手。
看了一下题解,用分块来做。开始觉得应该就是传说中的块状链表了,但幸好本题无需删除等复杂操作,基本是静态的,所以壮了壮胆,写了一下。
做法:
把原区间分成sqrt(N)块,并使得每个块有序。对于每个操作,找出L和R所在的块,对于L所在的和R所在的两个“边界块”(或在同一块中),暴力即可,最后维护有序性;每一个“中间块”修改可以打标记(询问时加上即可),询问需要二分。
由此易得空间复杂度O(N),时间复杂度O(max(sqrt(N),Q)*sqrt(N)log(sqrt(N)))。很不错。
const size=1000;oo=1000000001;
var n,m,q,i,j,k,l,p,x,y,xx,xx2,yy,yy2,z,ans:longint;c,cc:char;
a,b,num:array[1..1000,1..1000] of longint;
en:array[1..1000] of longint;
procedure gx(i:longint);
var j:longint;
begin
for j:=1 to size do
b[i,num[i,j]]:=j;
end;
procedure sort(i,x,y:longint);
var u,v,mid,t:longint;
begin
u:=x;
v:=y;
mid:=a[i,(x+y) div 2];
repeat
while a[i,u]<mid do inc(u);
while a[i,v]>mid do dec(v);
if u<=v then
begin
t:=a[i,u];a[i,u]:=a[i,v];a[i,v]:=t;
t:=num[i,u];num[i,u]:=num[i,v];num[i,v]:=t;
inc(u);
dec(v);
end;
until u>v;
if u<y then sort(i,u,y);
if x<v then sort(i,x,v);
end;
function ask(i,x,y:longint):longint;
var j:longint;
begin
ask:=0;
for j:=x to y do if a[i,b[i,j]]+en[i]>=z then inc(ask);
end;
procedure change(i,x,y:longint);
var j:longint;
begin
for j:=x to y do a[i,b[i,j]]:=a[i,b[i,j]]+z;
sort(i,1,size);
gx(i);
end;
function ask2(x,y:longint):longint;
var i,l,r:longint;
begin
if x>y then exit(0);
ask2:=0;
for i:=x to y do
if z<=a[i,1]+en[i] then ask2:=ask2+size
else
if z>a[i,size]+en[i] then continue
else
begin
l:=1;r:=size;
while l<r do
begin
if z<=a[i,(l+r) div 2]+en[i] then r:=(l+r) div 2 else l:=(l+r) div 2+1;
end;
ask2:=ask2+size-l+1;
end;
end;
procedure change2(x,y:longint);
var i:longint;
begin
if x>y then exit;
for i:=x to y do en[i]:=en[i]+z;
end;
begin
assign(input,'magic.in');
assign(output,'magic.out');
reset(input);rewrite(output);
readln(n,q);
if n mod size=0 then m:=n div size else m:=n div size+1;
for i:=1 to m do
for j:=1 to size do
begin
if (i-1)*1000+j>n then a[i,j]:=oo else read(a[i,j]);
num[i,j]:=j;
end;
readln;
for i:=1 to m do begin sort(i,1,size);gx(i);end;
for l:=1 to q do
begin
readln(c,cc,x,y,z);
xx:=(x-1) div size+1;xx2:=x-(xx-1)*size;
yy:=(y-1) div size+1;yy2:=y-(yy-1)*size;
if c='A' then
if xx=yy then writeln(ask(xx,xx2,yy2))
else writeln(ask(xx,xx2,size)+ask(yy,1,yy2)+ask2(xx+1,yy-1))
else
if xx=yy then change(xx,xx2,yy2)
else begin change(xx,xx2,size);change(yy,1,yy2);change2(xx+1,yy-1);end;
end;
close(input);close(output);
end.

浙公网安备 33010602011771号