题意
给出一个数列,我们对它进行操作。
输入操作符
t
t
t
1、当
t
=
0
t=0
t=0时,这一行还有两个数
a
a
a,
b
b
b,表示编号为
a
a
a的部落增加了
b
b
b个人(如果
b
<
0
b<0
b<0则表示减少了
∣
b
∣
|b|
∣b∣个人)。
2、当
t
=
1
t=1
t=1时,这一行还有三个数
a
,
b
,
c
a,b,c
a,b,c,表示编号为
a
∼
b
a\sim b
a∼b的部落增加了
c
c
c个人(如果
c
<
0
c<0
c<0则表示减少了
∣
c
∣
|c|
∣c∣个人)。
3、当
t
=
2
t=2
t=2时,这一行还有两个数
a
,
b
,
a,b,
a,b,表示神格询问了编号为
a
∼
b
a\sim b
a∼b的部落现在的总人数。
4、当
t
=
3
t=3
t=3时,这一行还有两个数
a
,
b
,
a,b,
a,b,表示神格询问了编号为
a
∼
b
a\sim b
a∼b的部落人数的方差。
方差:如果这些数的平均数是
a
v
e
ave
ave,那么它们的方差是
(
x
1
−
a
v
e
)
2
+
(
x
2
−
a
v
e
)
2
+
⋯
+
(
x
n
−
1
−
a
v
e
)
2
+
(
x
n
−
a
v
e
)
2
n
\frac{(x_1-ave)^2+(x_2-ave)^2+⋯+(x_{n-1}-ave)^2+(x_n-ave)^2}{n}
n(x1−ave)2+(x2−ave)2+⋯+(xn−1−ave)2+(xn−ave)2。
思路
前三个操作是线段树的基础操作,我们着重处理第四个操作。
因为分母就是
n
n
n,所以我们看分子,把它拆成,
x
1
2
−
2
∗
x
1
∗
a
v
e
+
a
v
e
2
+
x
2
2
−
2
∗
x
2
∗
a
v
e
+
a
v
e
2
+
.
.
.
+
x
n
2
−
2
∗
x
n
∗
a
v
e
+
a
v
e
2
x1^2-2*x1*ave+ave^2+x2^2-2*x2*ave+ave^2+...+xn^2-2*xn*ave+ave^2
x12−2∗x1∗ave+ave2+x22−2∗x2∗ave+ave2+...+xn2−2∗xn∗ave+ave2
然后就可以变成,
(
x
1
2
+
x
2
2
+
.
.
.
+
x
n
2
)
−
2
(
x
1
+
x
2
+
.
.
.
+
x
n
)
a
v
e
+
(
数
的
个
数
)
a
v
e
2
(x1^2+x2^2+...+xn^2)-2(x1+x2+...+xn)ave+(数的个数)ave^2
(x12+x22+...+xn2)−2(x1+x2+...+xn)ave+(数的个数)ave2
因为我们的线段树维护了区间和,也可以求出区间平均值,那么我们还需要维护它们的平方和,那么如果区间操作了我们的平方和该怎么维护呢?
假设一开始我们的平方和为
a
2
+
b
2
+
c
2
.
.
.
a^2+b^2+c^2...
a2+b2+c2...,如果我们给这个区间加上
x
x
x,就变成
(
a
+
x
)
2
+
(
b
+
x
)
2
+
(
c
+
x
)
2
.
.
.
(a+x)^2+(b+x)^2+(c+x)^2...
(a+x)2+(b+x)2+(c+x)2...,我们又可以拆成,
a
2
+
2
a
x
+
x
2
+
b
2
+
2
b
x
+
x
2
+
c
2
+
2
c
x
+
x
2
a^2+2ax+x^2+b^2+2bx+x^2+c^2+2cx+x^2
a2+2ax+x2+b2+2bx+x2+c2+2cx+x2,可以发现比原来的平方和多了
2
(
a
+
b
+
c
)
x
+
(
数
的
个
数
)
x
2
2(a+b+c)x+(数的个数)x^2
2(a+b+c)x+(数的个数)x2,然后我们就可以直接敲线段树了。
代码
#include<cstdio>
struct node{
long long l, r, sum, add, pfh;
}tree[400001];
int n, q;
int a[100001];
void build(int p, int l, int r) {
tree[p].l = l;
tree[p].r = r;
if (l == r) {
tree[p].sum = a[l];
tree[p].pfh = a[l] * a[l];
return;
}
int mid = (l + r) / 2;
build(p * 2, l, mid);
build(p * 2 + 1, mid + 1, r);
tree[p].sum = tree[p * 2].sum + tree[p * 2 + 1].sum;
tree[p].pfh = tree[p * 2].pfh + tree[p * 2 + 1].pfh;
}
void spread(int p) {
if (tree[p].add) {
tree[p * 2].add += tree[p].add;
tree[p * 2 + 1].add += tree[p].add;
tree[p * 2].pfh += 2 * tree[p * 2].sum * tree[p].add + (tree[p * 2].r - tree[p * 2].l + 1) * tree[p].add * tree[p].add;
tree[p * 2 + 1].pfh += 2 * tree[p * 2 + 1].sum * tree[p].add + (tree[p * 2 + 1].r - tree[p * 2 + 1].l + 1) * tree[p].add * tree[p].add;
tree[p * 2].sum += tree[p].add * (tree[p * 2].r - tree[p * 2].l + 1);
tree[p * 2 + 1].sum += tree[p].add * (tree[p * 2 + 1].r - tree[p * 2 + 1].l + 1);
tree[p].add = 0;
}
}
void change(int p, int l, int r, int v) {
if (l <= tree[p].l && tree[p].r <= r) {
tree[p].pfh += 2 * tree[p].sum * v + (tree[p].r - tree[p].l + 1) * v * v;
tree[p].sum += v * (tree[p].r - tree[p].l + 1);
tree[p].add += v;
return;
}
spread(p);
int mid = (tree[p].l + tree[p].r) / 2;
if (l <= mid) change(p * 2, l, r, v);
if (r > mid) change(p * 2 + 1, l, r, v);
tree[p].sum = tree[p * 2].sum + tree[p * 2 + 1].sum;
tree[p].pfh = tree[p * 2].pfh + tree[p * 2 + 1].pfh;
}
long long ask(int p, int l, int r) {
if (l <= tree[p].l && tree[p].r <= r)
return tree[p].sum;
spread(p);
int mid = (tree[p].l + tree[p].r) / 2;
long long result = 0;
if (l <= mid) result += ask(p * 2, l, r);
if (r > mid) result += ask(p * 2 + 1, l, r);
return result;
}
long long askpfh(int p, int l, int r) {
if (l <= tree[p].l && tree[p].r <= r)
return tree[p].pfh;
spread(p);
int mid = (tree[p].l + tree[p].r) / 2;
long long result = 0;
if (l <= mid) result += askpfh(p * 2, l, r);
if (r > mid) result += askpfh(p * 2 + 1, l, r);
return result;
}
int main() {
scanf("%d %d", &n, &q);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
build(1, 1, n);
int op, x, y, z;
for (; q; q--) {
scanf("%d", &op);
if (op == 0) {
scanf("%d %d", &x, &y);
change(1, x, x, y);
} else if (op == 1) {
scanf("%d %d %d", &x, &y, &z);
change(1, x, y, z);
} else if (op == 2) {
scanf("%d %d", &x, &y);
printf("%d\n", ask(1, x, y));
} else if (op == 3) {
scanf("%d %d", &x, &y);
long long s1 = askpfh(1, x, y);
long long s2 = ask(1, x, y);
double pjs = (double)s2 / (y - x + 1);
double s3 = s1 - 2 * s2 * pjs + (y - x + 1) * pjs * pjs;
printf("%.10lf\n", s3 / (y - x + 1));
}
}
}