P1471 方差
三种操作:给区间 + k,查询区间
x
ˉ
\bar{x}
xˉ,查询区间方差
显然求平均数我们只需要维护区间和即可,但是维护区间方差比较困难
维护方差公式推导过程
S
2
=
1
n
∗
[
(
x
1
−
x
ˉ
)
2
+
(
x
2
−
x
ˉ
)
2
+
(
x
3
−
x
ˉ
)
2
+
.
.
.
+
(
x
n
−
x
ˉ
)
2
]
S^2=\frac{1}{n}*[(x_1-\bar{x})^2+(x_2-\bar{x})^2+(x_3-\bar{x})^2+...+(x_n-\bar{x})^2]
S2=n1∗[(x1−xˉ)2+(x2−xˉ)2+(x3−xˉ)2+...+(xn−xˉ)2]
展开导出:
S
2
=
1
n
∗
(
x
1
2
−
2
x
1
x
ˉ
+
x
ˉ
2
+
x
2
2
−
2
x
2
x
ˉ
+
x
ˉ
2
+
.
.
.
+
x
n
2
−
2
x
n
x
ˉ
+
x
ˉ
2
)
S^2=\frac{1}{n}*(x_1^2-2x_1\bar{x}+\bar{x}^2+x_2^2-2x_2\bar{x}+\bar{x}^2+...+x_n^2-2x_n\bar{x}+\bar{x}^2)
S2=n1∗(x12−2x1xˉ+xˉ2+x22−2x2xˉ+xˉ2+...+xn2−2xnxˉ+xˉ2)
因为:
n
x
ˉ
=
x
1
+
x
2
+
.
.
.
+
x
n
n\bar{x}=x_1+x_2+...+x_n
nxˉ=x1+x2+...+xn
所以,原式得:
S
2
=
1
n
∗
[
(
x
1
2
+
x
2
2
+
.
.
.
+
x
n
2
)
−
2
n
x
ˉ
2
+
n
x
ˉ
2
]
S^2=\frac{1}{n}*[(x_1^2+x_2^2+...+x_n^2)-2n\bar{x}^2+n\bar{x}^2]
S2=n1∗[(x12+x22+...+xn2)−2nxˉ2+nxˉ2]
即:
S
2
=
(
x
1
2
+
x
2
2
+
x
3
2
+
.
.
.
+
x
n
2
)
n
−
x
ˉ
2
S^2=\frac{(x_1^2+x_2^2+x_3^2+...+x_n^2)}{n}-\bar{x}^2
S2=n(x12+x22+x32+...+xn2)−xˉ2
由于我们可以用线段树求出
x
ˉ
\bar{x}
xˉ ,所以我们只需要考虑公式前面一部分,也就是维护区间平方和
得到这个公式我们求方差就有思路了,同时维护区间平方和和区间和即可
如何维护区间平方和
我们给区间每个数加
k
k
k,设
s
u
m
1
=
x
1
+
x
2
+
x
3
+
.
.
.
+
x
n
s
u
m
2
=
x
1
2
+
x
2
2
+
x
3
2
+
.
.
.
+
x
n
2
sum_1=x_1+x_2+x_3+...+x_n\\ sum_2=x_1^2+x_2^2+x_3^2+...+x_n^2
sum1=x1+x2+x3+...+xnsum2=x12+x22+x32+...+xn2
那么
(
x
1
+
k
)
2
+
(
x
2
+
k
)
2
+
(
x
3
+
k
)
2
+
.
.
.
+
(
x
n
+
k
)
2
=
(
x
1
2
+
2
k
x
1
+
k
2
)
+
(
x
2
2
+
2
k
x
2
+
k
2
)
+
.
.
.
+
(
x
n
+
2
k
x
n
+
k
2
)
=
(
x
1
2
+
x
2
2
+
x
3
2
+
.
.
.
+
x
n
2
)
+
2
k
(
x
1
+
x
2
+
x
3
+
.
.
.
+
x
n
)
+
n
k
2
=
s
u
m
2
+
2
k
∗
s
u
m
1
+
n
k
2
(x_1+k)^2+(x_2+k)^2+(x_3+k)^2+...+(x_n+k)^2\\ =(x_1^2+2kx_1+k^2)+(x_2^2+2kx_2+k^2)+...+(x_n+2kx_n+k^2)\\ =(x_1^2+x_2^2+x_3^2+...+x_n^2)+2k(x_1+x_2+x_3+...+x_n)+nk^2\\ =sum_2+2k*sum_1+nk^2
(x1+k)2+(x2+k)2+(x3+k)2+...+(xn+k)2=(x12+2kx1+k2)+(x22+2kx2+k2)+...+(xn+2kxn+k2)=(x12+x22+x32+...+xn2)+2k(x1+x2+x3+...+xn)+nk2=sum2+2k∗sum1+nk2
注意
n
n
n 是区间长度
得到这个公式我们就可以很好的写
u
p
d
a
t
e
update
update 和
p
u
s
h
d
o
w
n
pushdown
pushdown 函数啦~
注意
p
u
s
h
d
o
w
n
pushdown
pushdown 函数要先修改区间平方和,再修改区间和,因为区间平方和的修改需要用修改前的区间和
code:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e5 + 9;
ll n, m;
double a[maxn];
struct node
{
double sum, mark;
}t1[maxn << 2], t2[maxn << 2];
inline void pushdown(int p, int len)
{
//if(t1[p].mark != t2[p].mark) return; // 测试一下,显然 t1和t2 的 mark是完全一样的,不一样会因为这句wa掉
// 区间平方和
t2[p << 1].mark += t2[p].mark;
t2[p << 1].sum += t1[p << 1].sum * (t2[p].mark * 2) + (len - len / 2) * (t2[p].mark * t2[p].mark);
t2[p << 1 | 1].mark += t2[p].mark;
t2[p << 1 | 1].sum += t1[p << 1 | 1].sum * (t2[p].mark * 2) + (len / 2) * (t2[p].mark * t2[p].mark);
t2[p].mark = 0;
// 区间和
t1[p << 1].mark += t1[p].mark;
t1[p << 1].sum += (len - len / 2) * t1[p].mark;
t1[p << 1 | 1].mark += t1[p].mark;
t1[p << 1 | 1].sum += (len / 2) * t1[p].mark;
t1[p].mark = 0;
}
void build(int p = 1, int cl = 1, int cr = n)
{
if(cl == cr){
t1[p].sum = a[cl]; t2[p].sum= a[cl] * a[cl];return;
}
int mid = (cl + cr) >> 1;
build(p << 1, cl, mid);
build(p << 1 | 1, mid + 1, cr);
t1[p].sum = t1[p << 1].sum + t1[p << 1 | 1].sum;
t2[p].sum = t2[p << 1].sum + t2[p << 1 | 1].sum;
}
double query(node tree[], int l, int r, int p = 1, int cl = 1, int cr = n)
{
if(cl >= l && cr <= r) return tree[p].sum;
int mid = (cl + cr) >> 1;
if(tree[p].mark) pushdown(p, cr - cl + 1);
double ans = 0;
if(mid >= l) ans += query(tree, l, r, p << 1, cl, mid);
if(mid < r) ans += query(tree, l, r, p << 1 | 1, mid + 1, cr);
t1[p].sum = t1[p << 1].sum + t1[p << 1 | 1].sum;
t2[p].sum = t2[p << 1].sum + t2[p << 1 | 1].sum;
return ans;
}
void update(int l, int r, double d, int p = 1, int cl = 1, int cr = n)
{
if(cl >= l && cr <= r){
t2[p].mark += d;
t2[p].sum += t1[p].sum * (d * 2) + (cr - cl + 1) * (d * d);
t1[p].mark += d;
t1[p].sum += (cr - cl + 1) * d;
return;
}
if(t1[p].mark || t2[p].mark) pushdown(p, cr - cl + 1);
int mid = (cl + cr) >> 1;
if(mid >= l) update(l, r, d, p << 1, cl, mid);
if(mid < r) update(l, r, d, p << 1 | 1, mid + 1, cr);
t1[p].sum = t1[p << 1].sum + t1[p << 1 | 1].sum;
t2[p].sum = t2[p << 1].sum + t2[p << 1 | 1].sum;
}
void work()
{
cin >> n >> m;
for(int i = 1; i <= n; ++i) scanf("%lf", &a[i]);
build();
while(m--)
{
int op, l, r;
scanf("%d %d %d", &op, &l, &r);
if(op == 1){
double k;scanf("%lf", &k);update(l, r, k);
}
else if(op == 2){
double ans = query(t1, l, r) / (r - l + 1) * 1.0;
printf("%.4f\n", ans);
}
else {
double ans = query(t1, l, r) / (r - l + 1) * 1.0;
printf("%.4f\n", query(t2, l, r) / (r - l + 1) * 1.0 - ans * ans);
}
}
}
int main()
{
//ios::sync_with_stdio(0);
work();
return 0;
}