P1471 方差
分析
数据结构的题目,我们直接来分析操作。
操作 1:1 x y k ,表示将第 x 到第 y 项每项加上 k,k 为一实数。
操作 2:2 x y ,表示求出第 x 到第 y 项这一子数列的平均数。
操作 3:3 x y ,表示求出第 x 到第 y 项这一子数列的方差。
操作一:
没什么好说的,区间加。加个懒标记add就好啦。
我们主要看的是,区间加的操作,对其余我们要维护的变量的改变
操作二:
我们首先确定的是,我们需要维护一个avg或者sum,我个人比较喜欢维护sum,这样精度损失会小一些,同时对于区间加而言,我们维护sum更习惯一些。
这样再算一个区间的平均值是,直接用sum/len(len为区间长度)
操作三:
这题的重点是,如何求方差。但其实也不难,我们直接来推公式。
\[s^2 = \frac{\sum_{i=1}^{n}(a_i-\bar{a})^2}{n}
\]
\[s^2 = \frac{\sum_{i=1}^{n}a_i^2-2\bar{a}\sum_{i=1}^{n}a_i+n\bar{a}^2}{n}
\]
\[设sum1 = \sum_{i=1}^{n}a_i,sum2 = \sum_{i=1}^{n}a_i^2
\]
\[s^2 = \frac{sum2-\frac{2sum1^2}{n}+n(\frac{sum1}{n})^2}{n}
\]
\[s^2 = \frac{sum2-\frac{sum1^2}{n}}{n}
\]
则我们需要维护的就是sum1与sum2
而区间加对两者的影响,我们只说对sum2的。
\[\sum_{i=1}^{n}(a_i+x)^2
\]
拆开
\[\sum_{i=1}^{n}a_i^2+2x\sum_{i=1}^{n}a_i+nx^2
\]
则,\(sum2_{变化后的}=sum2_{变化前的}+2xsum1_{变化前的}+nx^2\)
结束啦,直接看代码。
Ac_code
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
struct Node
{
int l,r;
double sum1,sum2,tag;
}tr[N<<2];
double w[N];
int n,m;
void pushup(int u)
{
tr[u].sum1 = tr[u<<1].sum1 + tr[u<<1|1].sum1;
tr[u].sum2 = tr[u<<1].sum2 + tr[u<<1|1].sum2;
}
void build(int u,int l,int r)
{
if(l==r)
{
tr[u] = {l,r,w[l],w[l]*w[l],0};
return ;
}
tr[u] = {l,r,0,0,0};
int mid = l + r >> 1;
build(u<<1,l,mid),build(u<<1|1,mid+1,r);
pushup(u);
}
void pushdown(int u)
{
auto &root = tr[u],&left = tr[u<<1],&right = tr[u<<1|1];
if(root.tag)
{
auto k = root.tag;
left.sum2 += 2*k*left.sum1 + k*k*(left.r - left.l + 1);
left.sum1 += (left.r - left.l + 1)*k;
left.tag += k;
right.sum2 += 2*k*right.sum1 + k*k*(right.r - right.l + 1);
right.sum1 += (right.r - right.l + 1)*k;
right.tag += k;
root.tag = 0;
}
}
void modify(int u,int l,int r,double k)
{
if(l<=tr[u].l&&tr[u].r<=r)
{
tr[u].sum2 += 2*k*tr[u].sum1 + k*k*(tr[u].r - tr[u].l + 1);
tr[u].sum1 += (tr[u].r - tr[u].l + 1)*k;
tr[u].tag += k;
return ;
}
int mid = tr[u].l + tr[u].r >> 1;
pushdown(u);
if(l<=mid) modify(u<<1,l,r,k);
if(r>mid) modify(u<<1|1,l,r,k);
pushup(u);
}
double querysum1(int u,int l,int r)
{
if(l<=tr[u].l&&tr[u].r<=r) return tr[u].sum1;
int mid = tr[u].l + tr[u].r >> 1;
pushdown(u);
double res = 0;
if(l<=mid) res += querysum1(u<<1,l,r);
if(r>mid) res += querysum1(u<<1|1,l,r);
return res;
}
double querysum2(int u,int l,int r)
{
if(l<=tr[u].l&&tr[u].r<=r) return tr[u].sum2;
int mid = tr[u].l + tr[u].r >> 1;
pushdown(u);
double res = 0;
if(l<=mid) res += querysum2(u<<1,l,r);
if(r>mid) res += querysum2(u<<1|1,l,r);
return res;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%lf",w+i);
build(1,1,n);
while(m--)
{
int op,x,y;scanf("%d%d%d",&op,&x,&y);
double k;
if(op==1)
{
scanf("%lf",&k);
modify(1,x,y,k);
}
else if(op==2) printf("%.4lf\n",querysum1(1,x,y)/(y-x+1));
else
{
double sum1 = querysum1(1,x,y)/(y-x+1),sum2 = querysum2(1,x,y)/(y-x+1);
double ans = sum2-sum1*sum1;
printf("%.4lf\n",ans);
}
}
return 0;
}

浙公网安备 33010602011771号