[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.
posted @ 2011-10-04 21:24  FancyCoder0  阅读(257)  评论(0编辑  收藏  举报