https://www.luogu.org/problem/show?pid=1471
很久没打线段树果然炸了;
数组开小了;
以前我在洛谷发过题解说对于数组我们要怎么开;
比如n=1e5;
那么这个二叉树一定会有1e5个叶子节点;
2^17=131072;
说以我们如果建一颗满二叉树的话叶子节点必须要有131072个;
所以总结点有2^18=262144-1个;
那么我们直接开262144的数组就好了;
但是我现在发现在lazy标记下传的时候如果不判叶子节点的话,最终会访问到叶子节点下一个节点的;
所以我们还要开大一倍,变成524288;
当然我是随便开的;
没刻意压行全场最短
#include<bits/stdc++.h>
#define Ll long long
using namespace std;
const int N=1e5+5;
struct tree{int l,r;double tag,v[2];}T[N*8];
int n,m,x,y,k;
double z;
void up(int id){
T[id].v[0]=T[id*2].v[0]+T[id*2+1].v[0];
T[id].v[1]=T[id*2].v[1]+T[id*2+1].v[1];
}
void push(int id){
int x=id*2,y=x+1;double z=T[id].tag;
if(z==0)return;T[id].tag=0;
T[x].tag+=z;
T[x].v[1]+=z*z*(T[x].r-T[x].l+1)+2*T[x].v[0]*z;
T[x].v[0]+=z*(T[x].r-T[x].l+1);
T[y].tag+=z;
T[y].v[1]+=z*z*(T[y].r-T[y].l+1)+2*T[y].v[0]*z;
T[y].v[0]+=z*(T[y].r-T[y].l+1);
}
void make(int id,int l,int r){
T[id].l=l; T[id].r=r;
if(l==r){
double x;
scanf("%lf",&x);
T[id].v[0]=x;
T[id].v[1]=x*x;
return;
}
int mid=l+r>>1;
make(id*2 ,l,mid );
make(id*2+1,mid+1,r);
up(id);
}
void add(int id,int x,int y,double z){
push(id);
if(x<=T[id].l&&T[id].r<=y){
T[id].tag=z;
T[id].v[1]+=z*z*(T[id].r-T[id].l+1)+2*T[id].v[0]*z;
T[id].v[0]+=z*(T[id].r-T[id].l+1);
return;
}
if(T[id*2 ].r>=x)add(id*2 ,x,y,z);
if(T[id*2+1].l<=y)add(id*2+1,x,y,z);
up(id);
}
double out(int id,int x,int y,int k){
push(id);
if(x<=T[id].l&&T[id].r<=y)return T[id].v[k];
double ans=0;
if(T[id*2 ].r>=x)ans+=out(id*2 ,x,y,k);
if(T[id*2+1].l<=y)ans+=out(id*2+1,x,y,k);
return ans;
}
int main()
{
scanf("%d%d",&n,&m);
make(1,1,n);
while(m--){
scanf("%d",&k);
if(k==1)scanf("%d%d%lf",&x,&y,&z),add(1,x,y,z);
if(k==2)scanf("%d%d",&x,&y),printf("%.4lf\n",out(1,x,y,0)/(y-x+1.));
if(k==3){
scanf("%d%d",&x,&y);
double ba=out(1,x,y,0)/(y-x+1.);
printf("%.4lf\n",out(1,x,y,1)/(y-x+1.)-ba*ba);
}
}
}