• 【SDOI2017】相关分析(线段树)


    Description

    你需要维护一个长度为 (n) 的实数对的序列,第 (i) 个元素为 ((x_i, y_i))。现有 (m) 次操作:

    • ( exttt{1 L R}):设区间 ([L, R]) 的平均数 (ar x = frac{sum_{i=L}^R x_i}{R-L+1},ar y = frac{sum_{i=L}^R y_i}{R-L+1}),求

    [a = dfrac{sum_{i=L}^R (x_i - ar x)(y_i - ar y)}{sum_{i=L}^R (x_i - ar x)^2} ]

    的值。误差不超过 (10^{-5})

    • ( exttt{2 L R S T}):进行如下修改:

      (forall iin[L, R],quad x_i leftarrow x_i + S)

      (forall iin[L, R],quad y_i leftarrow y_i + T)

    • ( exttt{3 L R S T}):进行如下修改:

      (forall i in [L, R],quad x_i leftarrow i + S)

      (forall i in [L, R],quad y_i leftarrow i + T)

    Hint

    (1le n, mle 10^5, 0le |S|, |T|le 10^5, 0le |x_i|, |y_i|le 10^5)

    Solution

    一道细节很多的线段树练手题。

    在考虑怎么支持这些操作时不妨先看看我们需要维护些什么东西。拆开 (a) 这个柿子:

    (下面用 (sum) 代替 (sum_{i=L}^R),用 (n) 代替 (R - L + 1)

    [egin{aligned} a = & dfrac{sum (x_i - ar x)(y_i - ar y)}{sum (x_i - ar x)^2} = dfrac A B \ \ A = & sum (x_i y_i - x_i ar y - y_i ar x_i + ar x ar y) \ = & sum x_i y_i - ar ysum x_i - ar xsum y_i + nar x ar y \ = & sum x_i y_i - frac{1}{n}sum x_i sum y_i \ \ B = & sum (x_i^2 + {ar x}^2 - 2x_iar x) \ = & sum x_i^2 +n{ar x}^2 - 2ar xsum x_i \ = & sum x_i^2 - frac{1}{n}left(sum x_i ight)^2\ end{aligned} ]

    (ar x, ar y) 展开的话,可以发现我们需要维护四个东西:

    [egin{aligned} A = & oxed{sum x_i y_i} - frac{1}{n}oxed{sum x_i} imes oxed{sum y_i} \ B = & oxed{sum x_i^2} - frac{1}{n}left(sum x_i ight)^2\ end{aligned} ]

    整坨柿子瞬间简洁可做了。


    那么如何维护这四个信息呢?为了方便,我们用 (v_1, v_2, v_3, v_4) 分别代表 (sum x_i, sum y_i,sum x_i^2,sum x_i y_i)

    考虑修改操作之后这四个信息的变化:

    • 操作 (2)

    [egin{aligned} &sum x_i osum(x_i+S)=sum x_i+nS & v_1 o v_1 + nS \ &sum y_i osum(y_i+T)=sum y_i+nT & v_2 o v_2 + nT \ &sum x_i^2 osum(x_i+S)^2=sum x_i^2+nS^2+2Ssum x_i& v_3 o v_3+nS^2+2Sv_1\ &sum x_i y_i osum(x_i+S)(y_i+T)=sum x_i y_i+Tsum x_i+Ssum y_i+nST & v_4 o v_4+Tv_1+Sv_2+nST\ end{aligned} ]

    • 操作 (3)
    • (s_1 = sum_{i=L}^R i, s_2 = sum_{i=L}^R i^2)

    [egin{aligned} &sum x_i osum(i+S)=s_1+nS & v_1 o s_1 + nS \ &sum y_i osum(i+T)=s_1+nT & v_2 o s_1 + nT \ &sum x_i^2 osum(i+S)^2=s_2+nS^2+2Ss_1& v_3 o s_2+nS^2+2Ss_1\ &sum x_i y_i osum(i+S)(i+T)=s_2+(T+S)s_1+nST & v_4 o s_2+(T+S)s_1+nST\ end{aligned} ]

    于是就这样维护就行了,注意操作二的更新顺序。

    其中 (s_1, s_2) 的计算可以预处理,也可以直接使用公式:

    • (sum_{j=1}^i j = frac{i(i+1)}{2})
    • (sum_{j=1}^i j^2 = frac{i(i+1)(2i+1)}{6})

    Code

    /*
     * Author : _Wallace_
     * Source : https://www.cnblogs.com/-Wallace-/
     * Problem : SDOI2017 相关分析
     */
    #include <algorithm>
    #include <cmath>
    #include <cstdio>
    
    using namespace std;
    const int N = 1e5 + 5;
    const int S = N << 2;
    
    namespace calculator {
        inline double sum(const double& l, const double& r) {
            return (l + r) * (r - l + 1) / 2;
        }
        inline double sqrs(const double& p) {
            return p * (p + 1) * (2 * p + 1) / 6;
        }
    }
    
    struct dataType {
        double sumx, sumy, sqrs, muls;
        inline dataType()
        : sumx(0.0), sumy(0.0), sqrs(0.0), muls(0.0) { }
        inline dataType(const double& a, const double& b,
                        const double& c, const double& d)
        : sumx(a), sumy(b), sqrs(c), muls(d) { }
        inline dataType(const double& x, const double& y)
        : sumx(x), sumy(y), sqrs(x * x), muls(x * y) { }
        inline dataType operator + (const dataType& rhs) const {
            return dataType(this->sumx + rhs.sumx, this->sumy + rhs.sumy,
                            this->sqrs + rhs.sqrs, this->muls + rhs.muls);
        }
        inline void update(const dataType& rhs) {
            this->sumx += rhs.sumx, this->sumy += rhs.sumy;
            this->sqrs += rhs.sqrs, this->muls += rhs.muls;
        }
    };
    
    struct tagType {
        double S, T;
        inline tagType()
        : S(0.0), T(0.0) { }
        inline tagType(const double& s, const double& t)
        : S(s), T(t) { }
        inline bool operator == (const tagType& rhs) const {
            return (fabs(this->S - rhs.S) <= 1e-8) && (fabs(this->T - rhs.T) <= 1e-8);
        }
        inline bool operator != (const tagType& rhs) const {
            return !(*this == rhs);
        }
    };
    const tagType std_cov(-1e18, -1e18);
    const tagType std_add(0, 0);
    
    int L[S], R[S];
    dataType tr[S];
    tagType cov[S];
    tagType add[S];
    
    #define mid ((L[x] + R[x]) / 2)
    #define len double(R[x] - L[x] + 1)
    
    inline void pushup(int x) {
        tr[x] = tr[x << 1] + tr[x << 1 | 1];
    }
    inline void setCov(int x, const tagType& v) {
        double s1 = calculator::sum(L[x], R[x]);
        double s2 = calculator::sqrs(R[x]) - calculator::sqrs(L[x] - 1);
    
        tr[x].sqrs = s2 + len * v.S * v.S + 2 * v.S * s1;
        tr[x].muls = s2 + (v.S + v.T) * s1 + len * v.S * v.T;
        tr[x].sumx = s1 + len * v.S;
        tr[x].sumy = s1 + len * v.T;
        cov[x] = v, add[x] = std_add;
    }
    inline void setAdd(int x, const tagType& v) {
        tr[x].sqrs += v.S * v.S * len + 2 * v.S * tr[x].sumx;
        tr[x].muls += v.S * tr[x].sumy + v.T * tr[x].sumx + len * v.S * v.T;
        tr[x].sumx += v.S * len, tr[x].sumy += v.T * len;
        add[x].S += v.S, add[x].T += v.T;
    }
    
    inline void pushdown(int x) {
        if (cov[x] != std_cov) {
            setCov(x << 1, cov[x]);
            setCov(x << 1 | 1, cov[x]);
            cov[x] = std_cov;
        }
        if (add[x] != std_add) {
            setAdd(x << 1, add[x]);
            setAdd(x << 1 | 1, add[x]);
            add[x] = std_add;
        }
    }
    
    void build(int x, int l, int r, double* datx, double* daty) {
        L[x] = l, R[x] = r;
        cov[x] = std_cov, add[x] = std_add;
        if (l == r) {
            tr[x] = dataType(datx[l], daty[l]);
            return;
        }
        build(x << 1, l, mid, datx, daty);
        build(x << 1 | 1, mid + 1, r, datx, daty);
        pushup(x);
    }
    void modify(int x, int l, int r, void(*update)(int, const tagType&), const tagType& v) {
        if (l <= L[x] && R[x] <= r) return update(x, v);
        pushdown(x);
        if (l <= mid) modify(x << 1, l, r, update, v);
        if (r > mid) modify(x << 1 | 1, l, r, update, v);
        pushup(x);
    }
    void query(int x, int l, int r, dataType& ret) {
        if (l <= L[x] && R[x] <= r) return ret.update(tr[x]);
        if (l > R[x] || L[x] > r) return;
        pushdown(x), query(x << 1, l, r, ret), query(x << 1 | 1, l, r, ret);
    }
    
    #undef mid
    #undef len
    
    int n, Q;
    double x[N], y[N];
    
    signed main() {
        scanf("%d%d", &n, &Q);
        for (int i = 1; i <= n; i++) scanf("%lf", x + i);
        for (int i = 1; i <= n; i++) scanf("%lf", y + i);
    
        build(1, 1, n, x, y);
        
        while (Q--) {
            int opt, l, r;
            scanf("%d%d%d", &opt, &l, &r);
    
            if (opt == 1) {
                dataType res; query(1, l, r, res);
                double len = r - l + 1;
                double avex = res.sumx / len;
                double avey = res.sumy / len;
                
                double A = res.muls - res.sumx * res.sumy / len;
                double B = res.sqrs - res.sumx * res.sumx / len;
                
                printf("%lf
    ", A / B);
            } else {
                tagType val; scanf("%lf%lf", &val.S, &val.T);
                modify(1, l, r, (opt == 2 ? setAdd : setCov), val);
            }
        }
    }
    
  • 相关阅读:
    ThinkPHP 实现数据库事务回滚示例代码
    Java数据结构和算法
    Java数据结构和算法
    Java数据结构和算法
    git push每次提交都要输入用户名的解决方案
    Java数据结构与算法
    Java数据结构和算法
    Java数据结构和算法
    类和接口
    git如何忽略文件
  • 原文地址:https://www.cnblogs.com/-Wallace-/p/13797527.html
Copyright © 2020-2023  润新知