【线段树 数学】JZOJ_4231 寻找神格

题意

给出一个数列,我们对它进行操作。
输入操作符 t t t
1、当 t = 0 t=0 t=0时,这一行还有两个数 a a a b b b,表示编号为 a a a的部落增加了 b b b个人(如果 b &lt; 0 b&lt;0 b<0则表示减少了 ∣ b ∣ |b| b个人)。
2、当 t = 1 t=1 t=1时,这一行还有三个数 a , b , c a,b,c abc,表示编号为 a ∼ b a\sim b ab的部落增加了 c c c个人(如果 c &lt; 0 c&lt;0 c<0则表示减少了 ∣ c ∣ |c| c个人)。
3、当 t = 2 t=2 t=2时,这一行还有两个数 a , b , a,b, ab表示神格询问了编号为 a ∼ b a\sim b ab的部落现在的总人数。
4、当 t = 3 t=3 t=3时,这一行还有两个数 a , b , a,b, ab表示神格询问了编号为 a ∼ b a\sim b ab的部落人数的方差。
方差:如果这些数的平均数是 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(x1ave)2+(x2ave)2++(xn1ave)2+(xnave)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 x122x1ave+ave2+x222x2ave+ave2+...+xn22xnave+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));
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值