zoukankan      html  css  js  c++  java
  • P5142 区间方差 题解

    题目传送门

    简述题意

    给一段序列,支持区间求方差,单点修改

    前置芝士

    • 线段树/树状数组/分块
    • 逆元
    • 推式子

    Solution

    题目中已经给出了方差的求法,我们这里设 (len) 表示要求的区间长度:

    [d = frac{1}{len} sum_{i = l}^{r} (a_i - overline{a})^2 ]

    看上去不好维护,让我们化简试试:

    [d = frac{1}{len} sum_{i = l}^{r} (a_i^2 - 2 cdot a_i cdot overline{a} + overline{a}^2) ]

    直接利用乘法分配律将其分开:

    [d = frac{1}{len} sum_{i = l}^{r} a_i^2 - frac{1}{len} sum_{i = l}^{r} 2 cdot a_i cdot overline{a} + frac{1}{len} sum_{i = l}^{r} overline{a}^2 ]

    很简单的昂,把无关项提出来,得到:

    [d = frac{1}{len} sum_{i = l}^{r} a_i^2 - 2 cdot overline{a} cdot frac{1}{len} sum_{i = l}^{r} a_i + overline{a}^2 ]

    因为 (overline{a} = frac{1}{len} sum_{i = l}^{r} a_i),所以继续化简可以得到:

    [d = frac{1}{len} sum_{i = l}^{r} a_i^2 - 2 cdot overline{a}^2 + overline{a}^2 ]

    [d = frac{1}{len} sum_{i = l}^{r} a_i^2 - overline{a}^2 ]

    然后直接pia的一下,把线段树扔上去,维护个区间和和区间平方和,连Push_down都不需要写,就做完了。

    但是!!

    我们知道方差经常是分数形式,并且这还是在模意义下。

    那么求出分母的逆元就好了,具体原理见有理数取余

    求逆元有两种方法(我们定义 (a) 的逆元为 (a^{-1})):

    • 费马小定理:(a^{-1} = a^{p - 2} mod {p})

    • 线性求逆元递推公式:
      $inv_i = (p - frac{p}{i}) imes inv_{p mod i} mod p $,证明的话可以看这道题

    Tips:

    • 记得开 long long
    • 取模要彻底。

    剩下的看代码吧。

    Code

    /*
    Work by: Suzt_ilymics
    Problem: P5142 区间方差
    Knowledge: 线段树
    Time: O(nlogn)
    */
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define LL long long
    #define int long long
    #define orz cout<<"lkp AK IOI!"<<endl
    
    using namespace std;
    const int MAXN = 1e5+5;
    const int INF = 1e9+7;
    const int mod = 1e9+7;
    
    int n, m;
    int a[MAXN], inv[MAXN];
    
    int read(){
        int s = 0, f = 0;
        char ch = getchar();
        while(!isdigit(ch))  f |= (ch == '-'), ch = getchar();
        while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
        return f ? -s : s;
    }
    
    namespace Seg{
        #define lson i << 1
        #define rson i << 1 | 1
        struct Tree{ int val, sum; }tree[MAXN << 2];
        void Push_up(int i) { // 更新 
            tree[i].val = (tree[lson].val + tree[rson].val) % mod;
            tree[i].sum = (tree[lson].sum + tree[rson].sum) % mod;
        } 
        void Build(int i, int l, int r) { // 建树 
            if(l == r) { tree[i].val = a[l] % mod, tree[i].sum = a[l] * a[l] % mod; return ; }
            int mid = (l + r) >> 1;
            Build(lson, l, mid), Build(rson, mid + 1, r);
            Push_up(i);
        }
        void Modify(int i, int l, int r, int L, int R, int val_) { // 单点修改,粘的模板所以写的区间修改的形式 
            if(L <= l && r <= R) { tree[i].val = val_ % mod, tree[i].sum = val_ * val_ % mod; return ; }
            int mid = (l + r) >> 1;
            if(mid >= L) Modify(lson, l, mid, L, R, val_);
            if(mid < R) Modify(rson, mid + 1, r, L, R, val_);
            Push_up(i);
        }
        int Query1(int i, int l, int r, int L, int R) { // 区间和 
            if(L <= l && r <= R) return tree[i].val;
            int mid = (l + r) >> 1, ans = 0;
            if(mid >= L) ans += Query1(lson, l, mid, L, R);
            if(mid < R) ans += Query1(rson, mid + 1, r, L, R);
            return ans % mod;
        }
        int Query2(int i, int l, int r, int L, int R) { // 区间平方和 
            if(L <= l && r <= R) return tree[i].sum;
            int mid = (l + r) >> 1, ans = 0;
            if(mid >= L) ans += Query2(lson, l, mid, L, R);
            if(mid < R) ans += Query2(rson, mid + 1, r, L, R);
            return ans % mod;
        }
    }
    
    void Init() {
        inv[0] = inv[1] = 1; // 线性求逆元,注意初始化 
        for(int i = 2; i <= n; ++i) inv[i] = (mod - mod / i) * inv[mod % i] % mod;
    }
    
    int Quick_Pow(int x, int p, int mod) { // 快速幂(然而没用到 
        int res = 1;
        while(p) {
            if(p & 1) res = res * x % mod;
            x = x * x % mod;
            p >>= 1;
        }
        return res;
    }
    
    signed main()
    {
        n = read(), m = read();
        Init();
        for(int i = 1; i <= n; ++i) a[i] = read();
        Seg::Build(1, 1, n);
        for(int i = 1, opt, l, r; i <= m; ++i) {
            opt = read(), l = read(), r = read();
            if(opt == 1) Seg::Modify(1, 1, n, l, l, r);
            else {
                int len = r - l + 1;
                int sum1 = Seg::Query1(1, 1, n, l, r) * inv[len] % mod; 
                int sum2 = Seg::Query2(1, 1, n, l, r) * inv[len] % mod;
                printf("%lld
    ", (sum2 - sum1 * sum1 % mod + mod) % mod);
            }
        }
        return 0;
    }
    
    
  • 相关阅读:
    CF 461B Appleman and Tree
    POJ 1821 Fence
    NOIP 2012 开车旅行
    CF 494B Obsessive String
    BZOJ2337 XOR和路径
    CF 24D Broken robot
    POJ 1952 BUY LOW, BUY LOWER
    SPOJ NAPTIME Naptime
    POJ 3585
    CF 453B Little Pony and Harmony Chest
  • 原文地址:https://www.cnblogs.com/Silymtics/p/14715848.html
Copyright © 2011-2022 走看看