zoukankan      html  css  js  c++  java
  • 模板

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

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

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

    单点加值,区间查询:

    初始化的时候手动把每一个值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

  • 相关阅读:
    PostgreSQL中的partition-wise join
    Partition-wise join
    外观模式 门面模式 Facade 结构型 设计模式(十三)
    桥接模式 桥梁模式 bridge 结构型 设计模式(十二)
    组合模式 合成模式 COMPOSITE 结构型 设计模式(十一)
    创建型设计模式对比总结 设计模式(八)
    原型模式 prototype 创建型 设计模式(七)
    单例模式 创建型 设计模式(六)
    建造者模式 生成器模式 创建型 设计模式(五)
    抽象工厂模式 创建型 设计模式(四)
  • 原文地址:https://www.cnblogs.com/Yinku/p/10473030.html
Copyright © 2011-2022 走看看