<线段树>寻找神格

来源:互联网 发布:安卓聊天软件源码 编辑:程序博客网 时间:2024/04/30 04:11

题意

让你维护4种操作:
1.两个数a,b,表示编号为a的部落增加了b个人(如果b<0则表示减少了|b|个人)。
2.三个数a,b,c,表示编号为a~b的部落增加了c个人(如果c<0则表示减少了|c|个人)。
3.两个数a,b,表示神格询问了编号为a~b的部落现在的总人数。
4.两个数a,b,表示神格询问了编号为a~b的部落人数的方差。
方差的定义:
求N个数的方差,设这N个数的平均数为ave,第i个数为x_i
=1/n[(x1ave)2+(x2ave)2++(x(n1)ave)2+(xnave)2]

数据范围

对于30%的数据,N≤1000,Q≤1000
对于100%的数据,1≤N≤100000,1≤Q≤100000,|a_i |≤1000,数据保证在任何时候|所有部落总人数|≤〖10〗^9

分析

这题明显的线段树维护,那么前三个操作很好处理,维护总和sum,lazy标记即可。那么第四个操作方差,可以发现ave(即平均数)可以由sum/num(总和/个数),那么我们可以多维护一个数就是这些数的平方和tot。至于方差可以用我们维护的这几个数来求。平方和的维护可以在1,2操作里分解成,1操作比较简单,直接拆开加上加的数b的平方还有2ba,a是原来的平方,那么2操作一样相当于有(l-r+1)个1操作即a2+b2+2ab我们可以把2abb2一起处理,即(lr+1)b2+2totb
这样便可以很好的解决问题了!

补充lazy操作

其实就是一种不到万不得已不更新的一种操作。当更新到k这个点时,先把k更新一次,再在它的标记里面更新(即它的儿子要加多少)。而只有当到了它的儿子时才继续更新,所以这可以大大加速,把很多无用的更新给免去。

var    t:array[0..1000000,1..3] of extended;    i,n,m,q,j,z,x,y:longint;an,bn,cn,ans,sum,k:extended;a:array[1..100000] of longint;procedure make(k,l,r:longint);var mid:longint;begin        mid:=(l+r)shr 1;        t[k*2,3]:=t[k*2,3]+t[k,2]*t[k,2]*(mid-l+1)+t[k*2,1]*2*t[k,2];        t[k*2+1,3]:=t[k*2+1,3]+t[k,2]*t[k,2]*(r-mid)+t[k*2+1,1]*2*t[k,2];        t[k*2,1]:=t[k*2,1]+(mid-l+1)*t[k,2];        t[k*2+1,1]:=t[k*2+1,1]+(r-mid)*t[k,2];        t[k*2,2]:=t[k*2,2]+t[k,2];        t[k*2+1,2]:=t[k*2+1,2]+t[k,2];        t[k,2]:=0;end;procedure add(k,l,r,x,y:longint;z:extended);var mid:longint;begin    if (l=x)and(r=y) then begin        t[k,3]:=t[k,3]+(y-x+1)*z*z+2*t[k,1]*z;        t[k,1]:=t[k,1]+z*(y-x+1);        t[k,2]:=t[k,2]+z;//lazy 操作    end    else begin       mid:=(l+r)shr 1;       make(k,l,r);//传递标记       if mid<x then add(k*2+1,mid+1,r,x,y,z)       else if mid>=y then add(k*2,l,mid,x,y,z)       else begin          add(k*2,l,mid,x,mid,z);          add(k*2+1,mid+1,r,mid+1,y,z);       end;       t[k,1]:=t[k*2,1]+t[k*2+1,1];       t[k,3]:=t[k*2,3]+t[k*2+1,3];    end;end;procedure find(k,l,r,x,y,z:longint);var mid:longint;begin    if (l=x)and(r=y) then begin       an:=an+t[k,1];bn:=bn+t[k,3];    end else begin        mid:=(l+r)shr 1;        make(k,l,r);//传递标记        if mid<x then find(k*2+1,mid+1,r,x,y,z)        else if mid>=y then find(k*2,l,mid,x,y,z)        else begin            find(k*2,l,mid,x,mid,z);            find(k*2+1,mid+1,r,mid+1,y,z);        end;        t[k,1]:=t[k*2,1]+t[k*2+1,1];        t[k,3]:=t[k*2,3]+t[k*2+1,3];    end;end;begin    readln(n,m);    for i:=1 to n do begin read(x);add(1,1,n,i,i,x);end;    for i:=1 to m do begin        read(z);        case z of        0:begin readln(x,y);add(1,1,n,x,x,y);end;        1:begin readln(x,y,k);add(1,1,n,x,y,k);end;        2:begin bn:=0;an:=0;readln(x,y);find(1,1,n,x,y,0);writeln(an:0:0);end;        3:begin bn:=0;an:=0;readln(x,y);        find(1,1,n,x,y,1);        cn:=an/(y-x+1);        writeln((bn+(y-x+1)*cn*cn-2*cn*an)/(y-x+1):0:4);end;        end;    end;end.
0 0
原创粉丝点击