题目链接:https://www.luogu.org/problemnew/show/P1471
线段树的想法很明显,重点在于如何维护方差?
直接维护是不可能的,利用数学公式展开。
自己吧,反正展开后就是维护 区间和 和 区间平方和 。
区间平方和如何pushdown?自己推吧,反正到最后是这个东西。
设 x 某子节点的左右边界为
L
L
L 和
R
R
R,区间和为
S
S
S,区间平方和为
S
2
S2
S2
那么
S
2
+
=
x
.
l
a
z
y
×
S
×
2
+
x
.
l
a
z
y
2
×
(
R
−
L
+
1
)
S2+=x.lazy×S×2+x.lazy^2×(R-L+1)
S2+=x.lazy×S×2+x.lazy2×(R−L+1)然后
S
+
=
x
.
l
a
z
y
∗
(
R
−
L
+
1
)
S+=x.lazy*(R-L+1)
S+=x.lazy∗(R−L+1)
最后询问方差的时候,设这个区间的元素个数为 k k k,区间和为 S 1 S1 S1,平方和为 S 2 S2 S2,输出 S 2 k − S 1 2 k 2 \frac{S2}k-\frac{S1^2}{k^2} kS2−k2S12
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1e5+5,maxm=maxn;
typedef double DB;
int n,m;
DB a[maxn];
struct js{
int L,R;
DB s,s2,z;
}t[maxn<<2];
int rad()
{
int ret=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
while (ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
return ret*f;
}
DB sq(DB x){return x*x;}
void build(int x,int L,int R)
{
t[x].L=L;t[x].R=R;
if (L==R)
{
t[x].s=a[L];t[x].s2=a[L]*a[L];
t[x].z=0;return;
}
int mid=L+R>>1;
build(x<<1,L,mid);
build(x<<1|1,mid+1,R);
t[x].s=t[x<<1].s+t[x<<1|1].s;
t[x].s2=t[x<<1].s2+t[x<<1|1].s2;
}
void pushdown(int x)
{
if (t[x].z==0) return;
DB L1=t[x<<1].R-t[x<<1].L+1,L2=t[x<<1|1].R-t[x<<1|1].L+1;
t[x<<1].z+=t[x].z;
t[x<<1|1].z+=t[x].z;
t[x<<1].s2+=sq(t[x].z)*L1+2*t[x<<1].s*t[x].z;
t[x<<1|1].s2+=sq(t[x].z)*L2+2*t[x<<1|1].s*t[x].z;
t[x<<1].s+=t[x].z*L1;
t[x<<1|1].s+=t[x].z*L2;
t[x].z=0;
}
void put(int x,int le,int ri,DB dt)
{
if (le>t[x].R||ri<t[x].L) return;
if (le<=t[x].L&&t[x].R<=ri)
{
t[x].z+=dt;
DB L=t[x].R-t[x].L+1;
t[x].s2+=sq(dt)*L+t[x].s*dt*2;
t[x].s+=dt*L;
return;
}
pushdown(x);
put(x<<1,le,ri,dt);
put(x<<1|1,le,ri,dt);
t[x].s=t[x<<1].s+t[x<<1|1].s;
t[x].s2=t[x<<1].s2+t[x<<1|1].s2;
}
DB get1(int x,int le,int ri)
{
if (le>t[x].R||ri<t[x].L) return 0;
if (le<=t[x].L&&t[x].R<=ri) return t[x].s;
pushdown(x);
return get1(x<<1,le,ri)+get1(x<<1|1,le,ri);
}
DB get2(int x,int le,int ri)
{
if (le>t[x].R||ri<t[x].L) return 0;
if (le<=t[x].L&&t[x].R<=ri) return t[x].s2;
pushdown(x);
return get2(x<<1,le,ri)+get2(x<<1|1,le,ri);
}
int main()
{
freopen("a2.in","r",stdin);freopen("a2.out","w",stdout);
n=rad();m=rad();
DB k,k1,k2;
for (int i=1;i<=n;++i) scanf("%lf",a+i);
build(1,1,n);
for (int i=1,pd,x,y;i<=m&&(pd=rad(),x=rad(),y=rad(),true);++i)
switch (pd)
{
case 1:scanf("%lf",&k);put(1,x,y,k);break;
case 2:printf("%.4lf\n",get1(1,x,y)/(y-x+1));break;
case 3:{
k=y-x+1;
k1=get1(1,x,y)/k;
k2=get2(1,x,y)/k;
printf("%.4lf\n",k2-sq(k1));
break;
}
}
return 0;
}