zoukankan      html  css  js  c++  java
  • [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.
    
  • 相关阅读:
    angularjs学习笔记—工具方法
    js日期格式转换的相关问题探讨
    vue路由原理剖析
    如何减少UI设计师产品与前端工程师的沟通成本
    前端优化带来的思考,浅谈前端工程化
    前端入门方法
    自写juqery插件实现左右循环滚动效果图
    前端大综合
    前端收集
    如何在代码中减少if else语句的使用
  • 原文地址:https://www.cnblogs.com/oldmanren/p/2199111.html
Copyright © 2011-2022 走看看