P1471 方差 (线段树维护区间方差,其实就是维护区间平方和

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[(x1xˉ)2+(x2xˉ)2+(x3xˉ)2+...+(xnxˉ)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(x122x1xˉ+xˉ2+x222x2xˉ+xˉ2+...+xn22xnxˉ+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+2ksum1+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;
}
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值