P1471 方差 线段树维护区间方差
P1471 方差 线段树维护区间方差
题意
线段树练习题,给定\(n\)个实数,\(m\)个询问,三种操作:
区间加\(k\)
区间均值
区间方差
\[1 \leq n \leq 1e5\\
1 \leq m \leq 1e5\\
\]
分析
显然区间均值是好维护的,只要求区间和就可以了。
区间均值直接看好像不好维护:于是拆开来看看
\[S^2 = \frac{1}{n} \sum(\overline x - x_i)^2\\
= \frac{1}{n} \sum (x_i^2 - 2x_i\overline x + \overline x^2)\\
= \frac{1}{n} (\sum x_i^2 - 2\overline x \sum x_i + n \overline x^2)\\
= \frac{\sum x_i^2}{n} - \overline x^2
\]
于是只需要维护区间平方和即可。
现在看看怎么区间修改
\[sum1 = \sum x_i\\
sum2 = \sum x_i^2\\
区间+k => \sum (x_i + k)^2 = \sum x_i^2 + 2k\sum x_i + nk^2 = sum2 + 2ksum1 + nk^2
\]
发现可以通过区间和维护
代码
#include<bits/stdc++.h>
#define eps 1e-8
#define equals(a,b) (fabs(a - b) < eps)
using namespace std;
typedef long long ll;
const ll MOD = 1e9 + 7;
ll rd(){
ll x = 0;
int f = 1;
char ch = getchar();
while(ch < '0' || ch > '9') {
if(ch == -1) f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9') {
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
struct SegmentTree{
int n;
vector<double> sum,ssum,tag;
SegmentTree(int n):n(n),sum(((n + 1) << 2)),ssum(((n + 1) << 2)),tag(((n + 1) << 2)) {}
int push_up(int i){
sum[i] = sum[i << 1] + sum[i << 1|1];
ssum[i] = ssum[i << 1] + ssum[i << 1|1];
}
void update(int i,int l,int r,double v){
ssum[i] += 2 * v * sum[i] + (r - l + 1) * v * v;
sum[i] += (r - l + 1) * v;
tag[i] += v;
}
void push(int i,int l,int r){
int mid = l + r >> 1;
if(!equals(tag[i],0)) {
update(i << 1,l,mid,tag[i]);
update(i << 1|1,mid + 1,r,tag[i]);
tag[i] = 0;
}
}
void update(int i,int l,int r,int L,int R,double v){
if(l > R || r < L) return;
if(l >= L && r <= R) return update(i,l,r,v);
int mid = l + r >> 1;
push(i,l,r);
update(i << 1,l,mid,L,R,v);
update(i << 1|1,mid + 1,r,L,R,v);
push_up(i);
}
double query1(int i,int l,int r,int L,int R){
if(l > R || r < L) return 0;
if(l >= L && r <= R) return sum[i];
int mid = l + r >> 1;
push(i,l,r);
return query1(i << 1,l,mid,L,R) + query1(i << 1|1,mid + 1,r,L,R);
}
double query2(int i,int l,int r,int L,int R){
if(l > R || r < L) return 0;
if(l >= L && r <= R) return ssum[i];
int mid = l + r >> 1;
push(i,l,r);
return query2(i << 1,l,mid,L,R) + query2(i << 1|1,mid + 1,r,L,R);
}
};
int main(){
int n = rd();
int m = rd();
SegmentTree seg(n);
for(int i = 1;i <= n;i++){
double x;
scanf("%lf",&x);
seg.update(1,1,n,i,i,x);
}
while(m--){
int op = rd();
if(op == 1) {
int x = rd();
int y = rd();
double v;
scanf("%lf",&v);
seg.update(1,1,n,x,y,v);
}
else if(op == 2) {
int x = rd();
int y = rd();
printf("%.4f\n",seg.query1(1,1,n,x,y) / (y - x + 1));
}
else {
int x = rd();
int y = rd();
double res = seg.query2(1,1,n,x,y);
double tmp = seg.query1(1,1,n,x,y);
res /= (y - x + 1);
tmp /= (y - x + 1);
res = res - tmp * tmp;
printf("%.4f\n",res);
}
}
}