洛谷P 1471分散(線分樹lazyメンテナンス区間平均数と区間分散)
34609 ワード
題意:転送ゲート題解:3つの操作:1つは区間加算、lazy操作で完成するのではなく、平均数、区間加算メンテナンス、3つは区間分散で、公式を展開します.発見s 2=1 n∗(Σi=x i=y a i 2−2∗a∗a∗Σi=x i=x i=y a i+(y−x+1)s^2=frac{ 1}{n}*(sum_{i=x}^{i=y}a_{i}^2−2*overline{a}*sum_{i=x}{a}****sum_{i=x}{i=x}*overline{a}*sum_{i=x}{i=y}a_{i}+(y−x+1)*overrll l l l l l l l l***overl l l l l l l l l l l l ine{a})s 2=n 1∗(Σi=xi=y ai 2−2∗a∗Σi=xi=y ai+(y−x+1)∗a)なので、さらに1つの区間二乗和を維持すればよい.
#include
using namespace std;
const int maxn=1e5+5;
int n,m,t,x,y;
double z;
struct data{
int l,r;
double sum,ssum,add;
}tree[maxn<<2];
void update(int k)
{
tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum;
tree[k].ssum=tree[k<<1].ssum+tree[k<<1|1].ssum;
}
void pushdown(int k,int x)
{
if(tree[k].add){
tree[k<<1].ssum+=2*tree[k].add*tree[k<<1].sum+tree[k].add*tree[k].add*(x-x/2);
tree[k<<1|1].ssum+=2*tree[k].add*tree[k<<1|1].sum+tree[k].add*tree[k].add*(x/2);
tree[k<<1].sum+=(x-x/2)*tree[k].add;
tree[k<<1|1].sum+=(x/2)*tree[k].add;
tree[k<<1].add+=tree[k].add;
tree[k<<1|1].add+=tree[k].add;
tree[k].add=0;
}
}
void build(int k,int s,int t)
{
tree[k].l=s;tree[k].r=t;
if(s==t){scanf("%lf",&tree[k].sum);tree[k].ssum=tree[k].sum*tree[k].sum;return ;}
int mid=(s+t)>>1;
build(k<<1,s,mid);
build(k<<1|1,mid+1,t);
update(k);
}
void change(int k,int p,int q,double y)
{
int l=tree[k].l,r=tree[k].r;
if(l==p&&r==q){
tree[k].ssum+=2*y*tree[k].sum+(r-l+1)*y*y;
tree[k].sum+=(r-l+1)*y;
tree[k].add+=y;
return ;
}
pushdown(k,r-l+1);
int mid=(l+r)>>1;
if(q<=mid)change(k<<1,p,q,y);
else if(p>mid)change(k<<1|1,p,q,y);
else{
change(k<<1,p,mid,y);
change(k<<1|1,mid+1,q,y);
}
update(k);
}
double ask1(int k,int p,int q)
{
int l=tree[k].l,r=tree[k].r;
if(l==p&&r==q)return tree[k].sum;
pushdown(k,r-l+1);
int mid=(l+r)>>1;
if(q<=mid)return ask1(k<<1,p,q);
else if(p>mid)return ask1(k<<1|1,p,q);
else{
return ask1(k<<1,p,mid)+ask1(k<<1|1,mid+1,q);
}
}
double ask2(int k,int p,int q)
{
int l=tree[k].l,r=tree[k].r;
if(l==p&&r==q)return tree[k].ssum;
pushdown(k,r-l+1);
int mid=(l+r)>>1;
if(q<=mid)return ask2(k<<1,p,q);
else if(p>mid)return ask2(k<<1|1,p,q);
else{
return ask2(k<<1,p,mid)+ask2(k<<1|1,mid+1,q);
}
}
int main()
{
scanf("%d%d",&n,&m);
build(1,1,n);
for(int i=0;i<m;i++){
scanf("%d",&t);
if(t==1){
scanf("%d%d%lf",&x,&y,&z);
change(1,x,y,z);
}else if(t==2){
scanf("%d%d",&x,&y);
double ans=ask1(1,x,y)/(y-x+1);
printf("%.4f
",ans);
}else{
scanf("%d%d",&x,&y);
double sum=ask1(1,x,y),ssum=ask2(1,x,y),ave=sum/(y-x+1);
double ans=(ssum-2*ave*sum+(y-x+1)*ave*ave)/(y-x+1);
printf("%.4f
",ans);
}
}
return 0;
}