有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.