zoukankan      html  css  js  c++  java
  • 树状数组模板(改点求段 / 该段求点 / 改段求段)

    1. 改点求段(单点更新, 区间求和)

    代码:

     1 #include <iostream>
     2 using namespace std;
     3 
     4 const int MAXN = 1e5 + 10;
     5 int tree[MAXN], n;
     6 
     7 int lowbit(int x){//返回 pow(2, k),其中k为末尾0的个数, 即返回最低位1的值
     8     return x & -x;
     9 }
    10 
    11 void add(int x, int d){//将d累加到tree数组对应位置
    12     while(x <= n){
    13         tree[x] += d;
    14         x += lowbit(x);
    15     }
    16 }
    17 
    18 int sum(int x){//向上求和, 返回[1, x]所有元素的和
    19     int ans = 0;
    20     while(x > 0){
    21         ans += tree[x];
    22         x -= lowbit(x);
    23     }
    24     return ans;
    25 }
    26 
    27 int main(void){
    28     int x;
    29     cin >> n;
    30     for(int i = 1; i <= n; i++){
    31         cin >> x;
    32         add(i, x);
    33     }
    34     for(int i = 1; i <= n; i++){
    35         cout << sum(i) << endl;
    36     }
    37     return 0;
    38 }
    View Code

    2. 改点求段(区间更新, 单点求值)

     用一个数组 d 存储目标数组 a 中相邻元素的差值, 即 i > 1 时, d[i] = a[i] - a[i - 1] ; i == 1 时, d[i] = a[i] .

    那么有 a[i] = d[1] + ... + d[i] .若要将 a 数组区间 [l, r] 的元素都加上 key, 显然只需令 d[l] += key, d[r + 1] -= key 即可.

    显然只要用树状数组维护一下 d 数组即可.

    代码:

     1 #include <iostream>
     2 #include <stdio.h>
     3 using namespace std;
     4 
     5 const int MAXN = 1e5 + 10;
     6 int  a[MAXN], tree[MAXN], d[MAXN], n;
     7 
     8 int lowbit(int x){
     9     return x & -x;
    10 }
    11 
    12 void add(int x, int ad){
    13     while(x <= n){
    14         tree[x] += ad;
    15         x += lowbit(x);
    16     }
    17 }
    18 
    19 int sum(int x){//sum(x)为a[x]的值
    20     int ans = 0;
    21     while(x > 0){
    22         ans += tree[x];
    23         x -= lowbit(x);
    24     }
    25     return ans;
    26 }
    27 
    28 int main(void){
    29     int x, q, l, r;
    30     cin >> n;
    31     for(int i = 1; i <= n; i++){
    32         scanf("%d", &a[i]);
    33         if(i == 1) d[i] = a[i];
    34         else d[i] = a[i] - a[i - 1];
    35     }
    36     for(int i = 1; i <= n; i++){
    37         add(i, d[i]);
    38     }
    39     cin >> q;
    40     while(q--){
    41         cin >> l >> r >> x;//将区间[l, r]的元素都加上x
    42         add(l, x);
    43         add(r + 1, -x);
    44         for(int i = 1; i <= n; i++){
    45             cout << sum(i) << " ";
    46         }
    47         cout << endl;
    48     }
    49 }
    View Code

    3. 改段求段

     与 2 中类似, 先开一个差分数组 d

    那么有:

    a1 + a2 + ... + an

    = d1 + (d1 + d2) + ... + (d1 + d2 + ... + dn)

    = n * d1 + (n - 1) * d2 + ... + dn

    = n * (d1 + d2 + ... + dn) - (0 * d1 + 1 * d2 + ... (n - 1) * dn)

    再令 c[i] = ( i - 1) * di

    那么原式可化简为:

     n * (d1 + d2 + ... + dn) - (c1 + c2 + ... cn)

    显然对于 d 和 c 数组求和可以用树状数组解决. 而由 2 可知 a 数组区间修改则只需要对 d 和 c 数组对应做单点修改即可.

    例题: http://codevs.cn/problem/1082/

    题意: 中文题诶~

    思路: 树状数组区间更新区间求和模板

    代码:

     1 #include <iostream>
     2 #include <stdio.h>
     3 #define ll long long
     4 using namespace std;
     5 
     6 const int MAXN = 2e5 + 10;
     7 ll a[MAXN], d[MAXN], c[MAXN];//d[i]=a[i]-a[i-1], c[i]=(i-1)*d[i]
     8 int n;
     9 
    10 int lowbit(int x){
    11     return x & -x;
    12 }
    13 
    14 void add(ll *tree, int x, ll ad){
    15     while(x <= n){
    16         tree[x] += ad;
    17         x += lowbit(x);
    18     }
    19 }
    20 
    21 ll sum(ll *tree, int x){
    22     ll ans = 0;
    23     while(x > 0){
    24         ans += tree[x];
    25         x -= lowbit(x);
    26     }
    27     return ans;
    28 }
    29 
    30 int main(void){
    31     ll ad;
    32     int q, op, l, r;
    33     scanf("%d", &n);
    34     for(int i = 1; i <= n; i++){
    35         scanf("%lld", &a[i]);
    36         add(d, i, a[i] - a[i - 1]);
    37         add(c, i, (i - 1) * (a[i] - a[i - 1]));
    38     }
    39     scanf("%d", &q);
    40     while(q--){
    41         scanf("%d", &op);
    42         if(op == 1){
    43             scanf("%d%d%lld", &l, &r, &ad);
    44             add(d, l, ad);
    45             add(d, r + 1, -ad);
    46             add(c, l, (l - 1) * ad);
    47             add(c, r + 1, -ad * r);
    48         }else{
    49             scanf("%d%d", &l, &r);
    50             ll sum1 = (l - 1) * sum(d, l - 1) - sum(c, l - 1);
    51             ll sum2 = r * sum(d, r) - sum(c, r);
    52             printf("%lld
    ", sum2 - sum1);
    53         }
    54     }
    55     return 0;
    56 }
    View Code
  • 相关阅读:
    2018暑假辽宁省集训划水记
    gcd以及exgcd入门讲解
    tuple必须加上逗号
    stressapptest工具
    Linux Centos7设置UTF-8编码,防止中文乱码
    [python3.7]列表
    【转载】]基于RedHatEnterpriseLinux V7(RHEL7)下SPEC CPU 2006环境搭建以及测试流程 介绍、安装准备、安装、config文件以及运行脚本介绍
    Linux Test Project(一)
    SPECCPU2006 Spec2006 使用说明
    测试用例使用!!!!!!!!!!!!!
  • 原文地址:https://www.cnblogs.com/geloutingyu/p/7230413.html
Copyright © 2011-2022 走看看