Loading

luogu P1471 方差

题目大意

要求我们找一个能够维护区间平均数,区间方差,区间加的数据结构

解法

很容易想到线段树
由于我们需要维护方差,所以我们要推式子
具体的:
\(S^2=\frac{1}{n}[(x_1-\overline{x})^2+(x_2-\overline{x})^2+(x_3-\overline{x})^2+...+(x_n-\overline{x})^2]\)
\(S^2=\frac{1}{n}[x_1^2+x_2^2+x_3^2+...+x_n^2-2(x_1\overline{x}+x_2\overline{x}+x_3\overline{x}+...+x_n\overline{x})+n\overline{x}^2]\)
因为\(n\overline{x}=x_1+x_2+x_3+...+x_n\) ,所以:
\(S^2=\frac{1}{n}[x_1^2+...+x_n^2-n\overline{x}^2]\)
\(S^2=\frac{x_1^2+...+x_n^2}{n}-\overline{x}^2\)
这就是我们的最终形式,其中若区间为\([l,r]\) ,区间平均值为\(\frac{\sum_{i=l}^{r}a_i}{r-l+1}\)
只需要用线段树额外维护区间和就可以了

那么区间加操作对区间平方和的影响呢?
令原来的 \(x_1^2+...+x_n^2=sum\) ,则
\((x_1+k)^2+(x_2+k)^2+...+(x_n+k)^2=sum+2k(x_1+...+x_n)+nk^2\)
就很容易维护了

代码

#include <cstdio>
#include <algorithm>
#include <iostream>

using namespace std;

typedef long double LD;

struct Node {
	int l , r;
	LD tag;
	LD sum1 , sum2;
};

const int N = 1e5+10;

int n , m;
LD g[N];
Node tr[N*4];

void pushup(int x) {
	tr[x].sum1 = tr[x<<1].sum1 + tr[x<<1|1].sum1;
	tr[x].sum2 = tr[x<<1].sum2 + tr[x<<1|1].sum2;
}

void pushdown(int x) {
	if(tr[x].tag != 0) {
		tr[x<<1].sum2 += 2*tr[x].tag*tr[x<<1].sum1+tr[x].tag*tr[x].tag*(tr[x<<1].r-tr[x<<1].l+1);
		tr[x<<1].sum1 += tr[x].tag*(tr[x<<1].r-tr[x<<1].l+1);
		tr[x<<1|1].sum2 += 2*tr[x].tag*tr[x<<1|1].sum1+tr[x].tag*tr[x].tag*(tr[x<<1|1].r-tr[x<<1|1].l+1);
		tr[x<<1|1].sum1 += tr[x].tag*(tr[x<<1|1].r-tr[x<<1|1].l+1);
		
		tr[x<<1].tag += tr[x].tag;
		tr[x<<1|1].tag += tr[x].tag;
		tr[x].tag = 0;
	}
	return;
}

void build(int x , int l , int r) {
	tr[x].l = l; tr[x].r = r;
	if(l == r) {
		tr[x].sum1 = g[l];
		tr[x].sum2 = g[l]*g[l];
		return;
	}
	
	int mid = (l+r)>>1;
	build(x<<1 , l , mid);
	build(x<<1|1 , mid+1 , r);
	pushup(x);
	return;
}

void modify(int x , int l , int r , LD k) {
	if(l <= tr[x].l && tr[x].r <= r) {
		tr[x].sum2 += tr[x].sum1*k*2+k*k*(tr[x].r-tr[x].l+1);
		tr[x].sum1 += k*(tr[x].r-tr[x].l+1);
		tr[x].tag += k;
		return;
	}
	
	pushdown(x);
	int mid = (tr[x].l+tr[x].r)>>1;
	if(l <= mid) modify(x<<1 , l , r , k);
	if(mid < r) modify(x<<1|1 , l , r , k);
	pushup(x);
	return;
}

LD query1(int x , int l , int r) {
	if(l <= tr[x].l && tr[x].r <= r) return tr[x].sum1;
	
	pushdown(x);
	int mid = (tr[x].l+tr[x].r)>>1;
	LD res = 0;
	if(l <= mid) res += query1(x<<1 , l , r);
	if(mid < r) res += query1(x<<1|1 , l , r);
	return res;
}

LD query2(int x , int l , int r) {
	if(l <= tr[x].l && tr[x].r <= r) return tr[x].sum2;
	
	pushdown(x);
	int mid = (tr[x].l+tr[x].r)>>1;
	LD res = 0;
	if(l <= mid) res += query2(x<<1 , l , r);
	if(mid < r) res += query2(x<<1|1 , l , r);
	return res;
}

int main() {
	scanf("%d%d" , &n , &m);
	for(int i = 1 ; i <= n ; i ++) 
		scanf("%Lf" , &g[i]);
	
	build(1 , 1 , n);
	
	while(m --) {
		int opt , x , y;
		scanf("%d%d%d" , &opt , &x , &y);
		if(opt == 1) {
			LD k; scanf("%Lf" , &k);
			modify(1 , x , y , k);
		}
		if(opt == 2) {
			LD t = query1(1 , x , y) / (1.0+y-x);
			printf("%.4Lf\n" , t);
		}
		if(opt == 3) {
			LD t = query2(1 , x , y) / (1.0+y-x);
			LD s = query1(1 , x , y) / (1.0+y-x);
			printf("%.4Lf\n" , t-s*s);
		}
	}
	return 0;
}
posted @ 2025-09-09 19:20  lyr2023  阅读(4)  评论(0)    收藏  举报