树状数组

树状数组基础

image

区间修改,单点查询:P3368 【模板】树状数组 2

题目描述

如题,已知一个数列,你需要进行下面两种操作:

  • 1 x y k:将区间 \([x, y]\) 内每个数加上 \(k\)
  • 2 x:输出第 \(x\) 个数的和。

数据范围
对于 \(100\%\) 的数据:\(1 \leq N, M\le 500000\)\(1 \leq x, y \leq n\),保证任意时刻序列中任意元素的绝对值都不大于 \(2^{30}\)

分析
区间修改,可以通过差分实现
单点查询,可以通过对差分数组的求前缀合,求出。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1e6+10;
int n,q,a[N];
LL tree[N];

#define lowbit(x)  (x&(-x))
void add(int x,int y) {
    while(x<=n) tree[x]+=y, x+=lowbit(x);
}
LL sum(int x) {
    LL ans=0;
    while(x) ans+=tree[x], x-=lowbit(x);
    return ans;
}
int main() {
    scanf("%d%d", &n,&q);
    for(int i=1; i<=n; i++) scanf("%d", &a[i]);
    for(int i=1; i<=n; i++) add(i, a[i]-a[i-1]);
    int op,l,r,x;
    while(q--) {
        scanf("%d %d", &op, &l);
        if(op==1) {
            scanf("%d%d", &r, &x);
            add(l, x), add(r+1, -x);
        } else printf("%lld\n", sum(l));
    }
    return 0;
}

区间修改,区间查询:P3372 【模板】线段树 1

题目描述

如题,已知一个数列,你需要进行下面两种操作:

  • 1 x y k:将区间 \([x, y]\) 内每个数加上 \(k\)
  • 2 x y:输出区间 \([x, y]\) 内每个数的和。

数据范围
对于 \(100\%\) 的数据:\(1 \le n, m \le {10}^5\)
保证任意时刻数列中所有元素的绝对值之和 \(\le {10}^{18}\)

分析
区间修改可以使用差分 \(D[i] = a[i]-a[i-1]\)
区间查询,使用区间和 \(sum(l,r)=sum(1,r)-sum(1,l-1)\)

差分的前缀和是原数组:\(a[i]=D[1]+D[2]+...+D[i]\)

\[\begin{aligned} sum(1,k) &= a[1]+a[2]+...+a[k] \\ \\ &= D[1] + D[1]+D[2] + D[1]+D[2]+D[3] +...+ D[1]+D[2]...+D[k]\\ \\ &= kD[1] + (k-1)D[2] + (k-2)D[3] + ... + (k-(k-1))D[k] \\ \\ &= k\sum_{i=1}^{k}D[i] - \sum_{i=1}^{k} {(i-1)D[i]} \end{aligned} \]

通过 2 个树状数组维护 \(D[i]\)\((i-1)D[i]\)
复杂度 \(O(mlog_2^n)\)


#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1e6+10;
LL n,q,a[N];
LL tr1[N], tr2[N];

#define lowbit(x)  (x&(-x))
void add(LL tr[],LL x,LL y) {
    while(x<=n) tr[x]+=y, x+=lowbit(x);
}
LL sum(LL tr[],LL x) {
    LL ans=0;
    while(x) ans+=tr[x], x-=lowbit(x);
    return ans;
}
LL ask(LL r) {
    return r*sum(tr1, r)-sum(tr2, r);
}
int main() {
    scanf("%lld%lld", &n,&q);
    for(int i=1; i<=n; i++) scanf("%lld", &a[i]);
    for(int i=1; i<=n; i++) {
        add(tr1, i, a[i]-a[i-1]);
        add(tr2, i, (a[i]-a[i-1])*(i-1));
    }
    LL op,l,r,x,ans;
    while(q--) {
        scanf("%lld%lld%lld", &op, &l, &r);
        if(op==1) {
            scanf("%lld", &x);
            add(tr1, l, x), add(tr1, r+1, -x);
            add(tr2, l, x*(l-1)), add(tr2, r+1, -x*(r+1-1));
        } else {
            ans = ask(r)-ask(l-1);
            printf("%lld\n", ans);
        }
    }
    return 0;
}
posted @ 2023-01-11 16:31  HelloHeBin  阅读(33)  评论(0)    收藏  举报