zoukankan      html  css  js  c++  java
  • P3368 (模板)树状数组2

      借这个题学新姿势,这个题需要利用差分才能AC,普通树状树有3个点过不了。

      差分原理(参考题解区大佬):

      一个例子,一组数据 $ a[] = { 1, 5, 4, 2, 3 } $,差分后得到 $ b[] = { 1, 4, -1, -2, 1 } $,其中 $ a_0 = 0, b_i = a_i - a_{i - 1} $,求原数组 $ a_n $ 某个位置 $ i $ 上的值。

      由 $ b_i = a_i - a_{i - 1} Rightarrow a_i = b_i + a_{i - 1} $,于是

    $$ left. egin{aligned} a_i &= b_i + a_{i - 1} \ a_{i - 1} &= b_{i - 1} + a_{i - 2} \ vdots \ a_1 &= b_1 + a_0 end{aligned} ight } + $$

      $ Rightarrow  a_i = b_i + b_{i - 1} + cdots + b_1 + a_0 $ ,注意到 $ a_0 = 0 $,于是 $ a_i = sum_{i = 1}^{n} b_i $ 。这样就求出了原数组位置上的值了。

      然后再看看如何更新区间的值呢。

      我们对 a 数组区间 2 ~ 4 每个值进行 +2 操作,得到 $ 1, 7, 6, 4, 3 $,我们对这个数组进行新的差分得到 $ b_n' = { 1 6 -1 -2 -1 } $ ,我们比较新的差分数组 $ b_n' $ 与 $ b_n $,发现只有 $ b_2',b_5' $ 上的值变了,$ b_2' = b_2 + 2, b_5' = b_5 - 2 $,可以验证,在任何区间 $ a[l,...,r] $ 做出 $ +x $ 更新,都有 $ b_l' = b_l + x , b_{r + 1}' = b_{r + 1} - x $ 。并且不论任何数组经过这样操作都有这样的特点,于是就有了代码中的 `dif()` 函数对区间进行更新。这样每次更新只用更新位置 $ b_l, b_{r + 1} $ 上的值,效率提高了许多。

    #include <bits/stdc++.h>
    #define MP make_pair
    #define PB push_back
    #define st first
    #define nd second
    #define rd third
    #define rg register
    #define FOR(i, a, b) for(int i =(a); i <=(b); ++i)
    #define RE(i, n) FOR(i, 1, n)
    #define FORD(i, a, b) for(int i = (a); i >= (b); --i)
    #define REP(i, n) for(int i = 0;i <(n); ++i)
    #define VAR(v, i) __typeof(i) v=(i)
    #define FORE(i, c) for(VAR(i, (c).begin()); i != (c).end(); ++i)
    #define ALL(x) (x).begin(), (x).end()
    #define SZ(x) ((int)(x).size())
    using namespace std;
    
    #define lowbit(x) ((x) & (-x))
    const int N = 500010;
    int id[N];
    void upd(int n, int k, int x)
    {
        while (k <= n) id[k] += x, k += lowbit(k);
    }
    void dif(int n, int l, int r, int x)
    {
        upd(n, l, x);
        upd(n, r + 1, -x);
    }
    int sum(int k)
    {
        int ans = 0;
        while (k > 0) ans += id[k], k -= lowbit(k);
        return ans;
    }
    int org(int k)
    {
        return sum(k) - sum(k - 1);
    }
    int ask(int l, int r)
    {
        return sum(r) - sum(l - 1);
    }
    int main()
    {
        int n, m, k, x, opera, l, r, pre;
        pre = 0;
        cin >> n >> m;
        FOR (i, 1, n)
        {
            cin >> x;
            upd(n, i, x - pre); // 差分后更新到树状数组
            pre = x;
        }
        while(m--)
        {
            cin >> opera;
            switch(opera)
            {
                case 1: cin >> l >> r >> x; dif(n, l, r, x); break;
                case 2: cin >> k; cout << sum(k) << endl; break;
            }
        }
        return 0;
    }
    

      

      

  • 相关阅读:
    Codeforces Gym
    洛谷试炼场一句话题解
    优先队列板子
    BZOJ 3524 [POI2014] Couriers/洛谷3567(可持久化线段树)
    hdu 4417 Super Mario(可持久化线段树)
    poj 2559 Largest Rectangle in a Histogram (单调栈)
    zoj 4019 Schrödinger's Knapsack
    hdu 1521 排列组合(指数型生成函数板子)
    hdu 2072 单词数(普通型生成函数板子)
    RabbitMQ广播:direct模式
  • 原文地址:https://www.cnblogs.com/darkchii/p/9678063.html
Copyright © 2011-2022 走看看