先说一下思路:
方差可以经过恒等变形变成
x12 + x22 + ... + xn2 + 2a(x1 + x2 + ... + xn) + na2
所以维护平方和、连续和即可
平均数我就不再推了……
天哪我连线段树都能写错!
写篇随笔记录一下我的易错点,顺便与大家交流一下……
void maintain(int L, int R, int o) { int M = L + R >> 1, lc = o << 1, rc = lc | 1, ln = M - L + 1, rn = R - M; sqrv[o] = sqrv[lc] + (addv[lc] * sumv[lc] << 1) + ln * addv[lc] * addv[lc] + sqrv[rc] + (addv[rc] * sumv[rc] << 1) + rn * addv[rc] * addv[rc]; sumv[o] = sumv[lc] + addv[lc] * ln + sumv[rc] + addv[rc] * rn; return ; }
在maintain函数中,须注意
sumv[o] = sumv[lc] + addv[lc] * ln + sumv[rc] + addv[rc] * rn;
不能偷懒,写成下面这样是错误的(想一想,为什么)
sumv[o] = sumv[lc] + sumv[rc] + addv[o] * (R - L + 1);
我解释一下:这样会将每段区间自己的addv[o]加上,那么query函数if(ql <= L && R <= qr)之中就不能加上add += addv[o]这句话了
void query(int L, int R, int o, LL add) { if(ql <= L && R <= qr) { add += addv[o]; int n = R - L + 1; _sum += sumv[o] + add * n; _sqr += sqrv[o] + (add * sumv[o] << 1) + n * add * add; } else { int M = L + R >> 1, lc = o << 1, rc = lc | 1; if(ql <= M) query(L, M, lc, add + addv[o]); if(qr > M) query(M+1, R, rc, add + addv[o]); } return ; }
(这是个人写线段树的习惯,习惯不一样的话易错点不再适用)
完整代码:
#include <iostream> #include <cstring> #include <cstdio> #include <cmath> #include <algorithm> #include <stack> #include <vector> #include <queue> #include <cstdlib> using namespace std; int read() { int x = 0, f = 1; char c = getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); } return x * f; } #define LL long long #define maxn 100010 struct Fraction { LL a, b; LL gcd(LL a, LL b) { return !b ? a : gcd(b, a % b); } Fraction maintain() { if(!a) { b = 1; return *this; } LL t = gcd(a, b); a /= t; b /= t; return *this; } Fraction operator - (const Fraction& t) const { Fraction ans = (Fraction){ a * t.b - t.a * b, b * t.b }; return ans.maintain(); } Fraction operator -= (const Fraction& t) { *this = *this - t; return *this; } void print() { printf("%lld/%lld ", a, b); return ; } } ; LL sumv[maxn*3], sqrv[maxn*3], addv[maxn*3], A[maxn]; void maintain(int L, int R, int o) { int M = L + R >> 1, lc = o << 1, rc = lc | 1, ln = M - L + 1, rn = R - M; sqrv[o] = sqrv[lc] + (addv[lc] * sumv[lc] << 1) + ln * addv[lc] * addv[lc] + sqrv[rc] + (addv[rc] * sumv[rc] << 1) + rn * addv[rc] * addv[rc]; sumv[o] = sumv[lc] + addv[lc] * ln + sumv[rc] + addv[rc] * rn; return ; } void build(int L, int R, int o) { if(L == R){ sumv[o] = A[L]; sqrv[o] = A[L] * A[R]; } else { int M = L + R >> 1, lc = o << 1, rc = lc | 1; build(L, M, lc); build(M+1, R, rc); maintain(L, R, o); } return ; } int ql, qr; LL v; void update(int L, int R, int o) { if(ql <= L && R <= qr) addv[o] += v; else { int M = L + R >> 1, lc = o << 1, rc = lc | 1; addv[lc] += addv[o]; addv[rc] += addv[o]; addv[o] = 0; if(ql <= M) update(L, M, lc); if(qr > M) update(M+1, R, rc); maintain(L, R, o); } return ; } LL _sum, _sqr; void query(int L, int R, int o, LL add) { if(ql <= L && R <= qr) { add += addv[o]; int n = R - L + 1; _sum += sumv[o] + add * n; _sqr += sqrv[o] + (add * sumv[o] << 1) + n * add * add; } else { int M = L + R >> 1, lc = o << 1, rc = lc | 1; if(ql <= M) query(L, M, lc, add + addv[o]); if(qr > M) query(M+1, R, rc, add + addv[o]); } return ; } int main() { int n = read(), m = read(); for(int i = 1; i <= n; i++) A[i] = read(); build(1, n, 1); while(m--) { int tp = read(); ql = read(); qr = read(); if(tp == 1) { v = read(); update(1, n, 1); } else { _sum = _sqr = 0; query(1, n, 1, 0); Fraction ans; LL tn = qr - ql + 1; if(tp == 2) { // average ans = (Fraction){ _sum, tn }; ans.maintain(); } else { // variance ans = (Fraction){ _sqr * tn - _sum * _sum, tn * tn }; ans.maintain(); } ans.print(); } } return 0; }