• 模板


    维护一些满足结合律又满足结合律的群,也就是交换群,与线段树不同,线段树只需要满足结合律,是幺半群

    原因在于线段树的区间操作是真正的从小区间合并上来的,但是树状数组是来源于两个前缀和作差,这就要求这个“前缀和”拥有逆元,也就是“减法”。

    这样,拥有逆元运算的就可以使用树状数组维护。

    单点加值,区间查询:

    初始化的时候手动把每一个值add上去就可以了。

    const int MAXN=500000;
    int a[MAXN+5];
    int c[MAXN+5];
    
    int n;
    
    inline int lowbit(int x) {
        return (x & -x);
    }
    
    //a[x]+=k
    void add(int x, int k) {
        for(int i=x;i<=n;i+=lowbit(i))
            c[i]+=k;
    }
    
    int get_sum(int x) {
        int ans = 0;
        for(int i=x;i;i-=lowbit(i))
            ans+=c[i];
        return ans;
    }
    
    inline int range_query(int l,int r) {
        return get_sum(r)-get_sum(l-1);
    }

    求逆序对:

    好像是求一整个数组的逆序对,留坑。

    求区间最大/最小值:

    这个是个假的区间最大/最小值,这个只能求[1,n],没啥用,不写了。

    区间加值,单点查询:

    利用差分的思想很容易实现,初始化的时候也可以每一个值手动add上去。

    const int MAXN=500000;
    int a[MAXN+5];
    int c[MAXN+5];
    
    int n;
    
    inline int lowbit(int x) {
        return (x & -x);
    }
    
    void add(int x, int k) {
        for(int i=x;i<=n;i+=lowbit(i))
            c[i]+=k;
    }
    
    void range_add(int l,int r,int k){
        add(l,k),add(r+1,-k);
    }
    
    int get_sum(int x) {
        int ans = 0;
        for(int i=x;i;i-=lowbit(i))
            ans+=c[i];
        return ans;
    }
    
    inline int query(int x) {
        return get_sum(x);
    }

    示例:

    记得是从1开始的。

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    
    const int MAXN=500000;
    int a[MAXN+5];
    int c[MAXN+5];
    
    int n;
    
    inline int lowbit(int x) {
        return (x & -x);
    }
    
    //a[x]+=k
    void add(int x, int k) {
        for(int i=x;i<=n;i+=lowbit(i))
            c[i]+=k;
    }
    
    int get_sum(int x) {
        int ans = 0;
        for(int i=x;i;i-=lowbit(i))
            ans+=c[i];
        return ans;
    }
    
    inline int range_query(int l,int r) {
        return get_sum(r)-get_sum(l-1);
    }
    
    
    int main() {
        int m;
        while(~scanf("%d%d",&n,&m)){
            memset(a,0,sizeof(a));
            memset(c,0,sizeof(c));
            for(int i=1;i<=n;i++){
                scanf("%d",&a[i]);
                add(i,a[i]);
            }
    
            while(m--){
                int ins;
                scanf("%d",&ins);
                if(ins==1){
                    int x,k;
                    scanf("%d%d",&x,&k);
                    add(x,k);
                }
                else{
                    int x,y;
                    scanf("%d%d",&x,&y);
                    printf("%d
    ",range_query(x,y));
                }
            }
        }
    }
    View Code

    区间加值,区间查询:

    考虑上面的区间加值,单点查询,我们要求的其实是query的前缀和,那么每个差分被用的次数是递减的。维护一个次数递增的差分和次数不变的差分,作差可以获得次数递减的差分。

    const int MAXN=500000;
    int a[MAXN+5];
    int c[MAXN+5];
    int c2[MAXN+5];
    
    int n;
    
    inline int lowbit(int x) {
        return (x & -x);
    }
    
    void add(int x, int k) {
        for(int i=x;i<=n;i+=lowbit(i))
            c[i]+=k,c2[i]+=k*x;
    }
    
    void range_add(int l,int r,int k){
        add(l,k),add(r+1,-k);
    }
    
    int get_sum(int x) {
        int ans = 0;
        for(int i=x;i;i-=lowbit(i))
            ans+=(x+1)*c[i]-c2[i];
        return ans;
    }
    
    inline int query(int l,int r) {
        return get_sum(r)-get_sum(l-1);
    }

     

    二维树状数组:

    留坑。

    参考资料:

    https://blog.csdn.net/bestsort/article/details/80796531

  • 相关阅读:
    线性回归和逻辑回归
    行列式,叉积 (2)
    K最邻近算法(K-Nearest Neighbor,KNN)(初探)
    线性感知机和SVM(初探)
    向量点积(Dot Product)
    聚类(初探)
    sqlserver全文检索
    诗词背诵
    初级英语04
    初级英语03
  • 原文地址:https://www.cnblogs.com/Yinku/p/10473030.html
Copyright © 2020-2023  润新知