[lnsyoj282/luoguP2122]还教室
题意
原题链接
给定序列a,要求处理区间加,区间查平均值,区间查方差
sol
显然线段树
区间加操作请参考[lnsyoj281/luoguP2023/AHOI2009]维护序列
区间查平均值
由于$$\overline a = \frac{\sum_{i=1}^{n} a_i}{n}$$
所以只需记录区间和即可
区间查方差
由于$$S^2 = \frac{\sum_{i=1}^{n} (a_i - \overline a)^2}{n}$$
可得$$S^2 = \frac{\sum_{i=1}^{n} a_i^2 - \sum_{i=1}^{n} 2 \cdot \overline a \cdot a_i + n \cdot \overline a}{n}$$
其中,\(\sum_{i=1}^{n} a_i\)及\(\sum_{i=1}^{n} a_i^2\)均能使用线段树处理,而\(\overline a\)的处理方式前面已经处理完毕,因此该式可以计算出来
区间查平方和
下面介绍\(\sum_{i=1}^{n} a_i^2\)的处理方式:
假设序列\(a\)的每个值都加上\(x\),则平方和的值由$$\sum_{i=1}^{n} a_i^2$$
变为$$\sum_{i=1}^{n} a_i^2 - \sum_{i=1}^{n} 2 \cdot x \cdot a_i + n \cdot x$$
因此,只需要在处理区间和之前,处理区间平方和即可
代码
注:因为蒟蒻懒得想为了更方便编写题解,蒟蒻在代码中单独编写了分数(Fraction)结构体,详见代码
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
typedef __int128 LL;
const int N = 100005;
int n, m;
LL lazyadd[N * 4];
int a[N];
struct Node{
LL sum = 0, square = 0;
}tree[N * 4];
LL lcm(LL x, LL y){
return x / __gcd(x, y) * y;
}
struct Fraction{ //分数结构体
LL x, y;
Fraction(LL x, LL y):x(x), y(y){}
void divide(){ //约分
if (!x) {
y = 1;
return ;
}
LL d = __gcd(x, y);
x /= d, y /= d;
}
Fraction operator-() const{
Fraction copy(-x, y);
return copy;
}
Fraction operator+(const Fraction &W) const{
LL yy = lcm(y, W.y);
LL atimes = yy / y, btimes = yy / W.y;
LL xx = x * atimes + W.x * btimes;
Fraction ans(xx, yy);
ans.divide();
return ans;
}
Fraction operator-(const Fraction &W) const{
Fraction copy(x, y);
return copy + (-W);
}
Fraction operator*(const Fraction &W) const{
Fraction ans(x * W.x, y * W.y);
ans.divide();
return ans;
}
Fraction operator*(const LL &W) const{
Fraction ans(x * W, y);
ans.divide();
return ans;
}
Fraction operator/(const LL &W) const{
Fraction ans(x, y * W);
ans.divide();
return ans;
}
void print(){
long long xx = x, yy = y;
printf("%lld/%lld\n", xx, yy);
}
};
void pushup(int u){
tree[u].sum = tree[u << 1].sum + tree[u << 1 | 1].sum;
tree[u].square = tree[u << 1].square + tree[u << 1 | 1].square;
}
void pushdown(int u, int l, int r){
int mid = l + r >> 1;
tree[u << 1].square += 2 * tree[u << 1].sum * lazyadd[u] + lazyadd[u] * lazyadd[u] * (mid - l + 1);
tree[u << 1 | 1].square += 2 * tree[u << 1 | 1].sum * lazyadd[u] + lazyadd[u] * lazyadd[u] * (r - mid);
tree[u << 1].sum += lazyadd[u] * (mid - l + 1);
tree[u << 1 | 1].sum += lazyadd[u] * (r - mid);
lazyadd[u << 1] += lazyadd[u];
lazyadd[u << 1 | 1] += lazyadd[u];
lazyadd[u] = 0;
}
void build(int u, int l, int r){
if (l == r) {
tree[u].sum = a[l];
tree[u].square = a[l] * a[l];
return ;
}
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
pushup(u);
}
void update(int u, int l, int r, int L, int R, int val){
if (L <= l && r <= R){
lazyadd[u] += val;
tree[u].square += 2 * tree[u].sum * val + val * val * (r - l + 1);
tree[u].sum += val * (r - l + 1);
return ;
}
pushdown(u, l, r);
int mid = l + r >> 1;
if (L <= mid) update(u << 1, l, mid, L, R, val);
if (R > mid) update(u << 1 | 1, mid + 1, r, L, R, val);
pushup(u);
}
LL query(int u, int l, int r, int L, int R, int op){ //op=0时,为查询区间和,op=1时,为查询区间平方和
if (L <= l && r <= R){
if (op) return tree[u].square;
return tree[u].sum;
}
pushdown(u, l, r);
LL res = 0, mid = l + r >> 1;
if (L <= mid) res += query(u << 1, l, mid, L, R, op);
if (R > mid) res += query(u << 1 | 1, mid + 1, r, L, R, op);
return res;
}
int main(){
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
build(1, 1, n);
while (m -- ){
int op;
int l, r, k;
scanf("%d%d%d", &op, &l, &r);
// puts("");
if (op == 1) {
scanf("%d", &k);
update(1, 1, n, l, r, k);
}
else {
LL sum = query(1, 1, n, l, r, 0), cnt = r - l + 1;
Fraction average(sum, cnt); //平均值
average.divide();
if (op == 2) average.print();
else {
LL Square = query(1, 1, n, l, r, 1);
Fraction square(Square, 1);
Fraction varience(1, cnt);
varience = varience * (square - average * 2 * sum + average * average * (r - l + 1)); //方差
varience.divide();
varience.print();
}
}
}
}