《P1471 方差》
题目背景
滚粗了的 HansBug 在收拾旧数学书,然而他发现了什么奇妙的东西。
题目描述
蒟蒻 HansBug 在一本数学书里面发现了一个神奇的数列,包含 N 个实数。他想算算这个数列的平均数和方差。
输入格式
第一行包含两个正整数 N,M,分别表示数列中实数的个数和操作的个数。
第二行包含 N 个实数,其中第 i 个实数表示数列的第 i 项。
接下来 M 行,每行为一条操作,格式为以下三种之一:
操作 1:1 x y k
,表示将第 x 到第 y 项每项加上 k,k 为一实数。
操作 2:2 x y
,表示求出第 x 到第 y 项这一子数列的平均数。
操作 3:3 x y
,表示求出第 x 到第 y 项这一子数列的方差。
输出格式
输出包含若干行,每行为一个实数,即依次为每一次操作 2 或操作 3 所得的结果(所有结果四舍五入保留 4 位小数)。
输入输出样例
输入 #1复制
5 5 1 5 4 2 3 2 1 4 3 1 5 1 1 1 1 1 2 2 -1 3 1 5
输出 #1复制
3.0000 2.0000 0.8000
说明/提示
关于方差:对于一个有 n 项的数列 A,其方差 s2 定义如下:
s2=n1i=1∑n(Ai−A)2
其中 A 表示数列 A 的平均数,Ai 表示数列 A 的第 i 项。
样例说明:
操作步骤 | 输入内容 | 操作要求 | 数列 | 输出结果 | 说明 |
---|---|---|---|---|---|
0 | - | - | 1 5 4 2 3 | - | - |
1 | 2 1 4 | 求 [1,4] 内所有数字的平均数 | 1 5 4 2 3 | 3.0000 | 平均数 =(1+5+4+2)÷4=3.0000 |
2 | 3 1 5 | 求 [1,5] 内所有数字的方差 | 1 5 4 2 3 | 2.0000 | 平均数 =(1+5+4+2+3)÷5=3,方差 =((1−3)2+(5−3)2+(4−3)2+(2−3)2+(3−3)2)÷5=2.0000 |
3 | 1 1 1 1 | 将 [1,1] 内所有数字加 1 | 2 5 4 2 3 | - | - |
4 | 1 2 2 -1 | 将 [2,2] 内所有数字加 −1 | 2 4 4 2 3 | - | - |
5 | 3 1 5 | 求 [1,5] 内所有数字的方差 | 2 4 4 2 3 | 0.8000 | 平均数 =(2+4+4+2+3)÷5=3,方差 =((2−3)2+(4−3)2+(4−3)2+(2−3)2+(3−3)2)÷5=0.8000 |
数据规模:
数据点 | N | M | 备注 |
---|---|---|---|
1∼3 | N≤8 | M≤15 | - |
4∼7 | N≤105 | M≤105 | 不包含操作 3 |
8∼10 | N≤105 | M≤105 | - |
代码实现;
#include <iostream>
#include <iomanip>
#include <vector>
using namespace std;
struct SegmentTreeNode {
int left, right; // 区间左右端点
double sum; // 区间和
double squareSum; // 区间平方和
SegmentTreeNode *leftChild, *rightChild;
SegmentTreeNode(int l, int r) : left(l), right(r), sum(0), squareSum(0) {
leftChild = NULL; // 使用NULL替代nullptr
rightChild = NULL; // 使用NULL替代nullptr
}
};
// 构建线段树
void build(SegmentTreeNode* node, const vector<double>& arr) {
if (node->left == node->right) {
node->sum = arr[node->left - 1];
node->squareSum = arr[node->left - 1] * arr[node->left - 1];
return;
}
int mid = (node->left + node->right) / 2;
node->leftChild = new SegmentTreeNode(node->left, mid);
node->rightChild = new SegmentTreeNode(mid + 1, node->right);
build(node->leftChild, arr);
build(node->rightChild, arr);
node->sum = node->leftChild->sum + node->rightChild->sum;
node->squareSum = node->leftChild->squareSum + node->rightChild->squareSum;
}
// 区间更新:将[L, R]区间内的每个数加k
void update(SegmentTreeNode* node, int L, int R, double k) {
if (node->right < L || node->left > R) return;
if (L <= node->left && node->right <= R) {
// 区间完全包含,更新和与平方和
node->sum += k * (node->right - node->left + 1);
node->squareSum += 2 * k * node->sum - k * k * (node->right - node->left + 1);
return;
}
update(node->leftChild, L, R, k);
update(node->rightChild, L, R, k);
node->sum = node->leftChild->sum + node->rightChild->sum;
node->squareSum = node->leftChild->squareSum + node->rightChild->squareSum;
}
// 区间查询:查询[L, R]的和与平方和
void query(SegmentTreeNode* node, int L, int R, double& sum, double& squareSum) {
if (node->right < L || node->left > R) return;
if (L <= node->left && node->right <= R) {
sum += node->sum;
squareSum += node->squareSum;
return;
}
query(node->leftChild, L, R, sum, squareSum);
query(node->rightChild, L, R, sum, squareSum);
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); // 用0替代nullptr
cout << fixed << setprecision(4);
int n, m;
cin >> n >> m;
vector<double> arr(n);
for (int i = 0; i < n; i++) {
cin >> arr[i];
}
// 构建线段树
SegmentTreeNode* root = new SegmentTreeNode(1, n);
build(root, arr);
// 处理操作
for (int i = 0; i < m; i++) {
int op;
cin >> op;
if (op == 1) {
int x, y;
double k;
cin >> x >> y >> k;
update(root, x, y, k);
} else if (op == 2) {
int x, y;
cin >> x >> y;
double sum = 0, squareSum = 0;
query(root, x, y, sum, squareSum);
double average = sum / (y - x + 1);
cout << average << endl;
} else if (op == 3) {
int x, y;
cin >> x >> y;
double sum = 0, squareSum = 0;
query(root, x, y, sum, squareSum);
int count = y - x + 1;
double average = sum / count;
double variance = (squareSum - count * average * average) / count;
cout << variance << endl;
}
}
return 0;
}