• 树状数组


    很久之前写过一篇

    只不过很短(我太弱了)

    倒还基础

    传送门

    1.单点查询+区间修改

    #include<cstdio>
    using namespace std;
    int n,m,u,x,y,z;
    int a[500005];
    
    int lowbit(int c)
    {
        return c &(-c);
    }
    
    void update(int c,int d)
    {
        while(c <= n)
        {
            a[c] += d;
            c += lowbit(c);
        }
    }
    
    int query(int c)
    {
        int sum = 0;
        while(c > 0)
        {
            sum += a[c];
            c -= lowbit(c);
        }
        return sum;
    }
    
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i = 1; i <= n; i++)
        {
            scanf("%d",&u);
            update(i,u);
        }
        for(int i = 1; i <= m; i++)
        {
            scanf("%d%d%d",&x,&y,&z);
            if(x == 1)
                update(y,z);
            if(x == 2)
                printf("%d
    ",query(z) - query(y-1));
        }
        return 0;
    }

    2.区间修改+单点查询

      差分的思想

    查询

    设原数组为a[ i ],

    设数组tree[ i ] = tree[ i ] - tree[i - 1](a[ 0 ] = 0)

    则a[ i ] = tree[1] + tree[2] +...tree[ i ];

    修改

    当给区间[ l,r ]加上x的时候时

    a[ l ]与前一个元素a[l - 1]的差增加了x

    a[r + 1]与a[r]的差减少了x

    根据tree[ i ]数组的定义

    只需要给a[ l ]加上x

    给a[r + 1]减去x即可

    #include <cstdio>
    #define lowbit(x) x & -x
    
    using namespace std;
    
    int tree[500005];
    int n, m;
    
    void add(int x,int num) 
    {
        while (x <= n) 
        {
            tree[x] += num;
            x += lowbit(x);
        }
    }
    
    int query(int x) 
    {
        int ans = 0;
        while (x) 
        {
            ans += tree[x];
            x -= lowbit(x);
        }
        return ans;
    }
    
    int main() 
    {
        scanf("%d%d", &n, &m);
        int last = 0, now;
        for (int i = 1; i <= n; i++) 
        {
            scanf("%d", &now);
            add(i, now - last);
            last = now;
        }
        int flg;
        while (m--) 
        {
            scanf("%d", &flg);
            if (flg == 1) 
            {
                int x, y;
                int k;
                scanf("%d%d%d", &x, &y, &k);
                add(x, k);
                add(y + 1, -k);
            } else 
            if (flg == 2) 
            {
                int x;
                scanf("%d", &x);
                printf("%d
    ", query(x));
            }
        }
        return 0;
    }

    3.区间修改+区间查询

    差分的思想

    查询

    位置p的前缀和即:(p + 1)* sum1数组中p的前缀和 - sum2数组中p的前缀和

    区间[ l , r]的和即:位置r的前缀和 - 位置l - 1的前缀和

    修改

    对于sum1数组的修改同2中d数组的修改

    对于sum2数组的修改也类似,给sum2[ l ]加上l * x,给sum2[r + 1]减去(r + 1)* x

    #include<cstdio>
    #include<algorithm>
    #define ll long long
    using namespace std;
    
    inline ll read()
    {
        ll sum = 0,p = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9')
        {
            if(ch == '0')
                p = -1;
            ch = getchar();
        }
        while(ch >= '0' && ch <= '9')
        {
            (sum *= 10) += ch - '0';
            ch = getchar();
        }
        return sum * p;
    }
    
    const ll maxn = 1e5 + 5 , maxm = 1e5 + 5;
    ll n,m;
    ll v[maxn],sum1[maxn],sum2[maxn];
    
    ll lowbit(ll o)
    {
        return o&(-o);
    }
    
    void update(ll x,ll k)
    {
        int i = x;
        while(x <= n)
        {
            sum1[x] += k;
            sum2[x] += i * k;
            x += lowbit(x);
        }
    }
    
    ll query(ll o)
    {
        ll ans = 0;
        for(ll i = o;i;i -= lowbit(i))
            ans += (o + 1) * sum1[i] - sum2[i];
        return ans;
    }
    
    int main()
    {
        n = read(),m = read();
        for(ll i = 1;i <= n;i++)
        {
            v[i] = read();
            update(i,v[i] - v[i - 1]);
        }
        for(ll i = 1;i <= m;i++)
        {
            ll opt = read(),x,y,k;
            if(opt == 1)
            {
                x = read(),y = read(),k = read();
                update(x,k);
                update(y + 1,-k);
            }
            if(opt == 2)
            {
                x = read(),y = read();
                printf("%lld
    ",query(y) - query(x - 1));
            }
        }
        return 0;
    }

    二维树状数组

    运用类比的思想(呕自己

    一维树状数组中tree[x]中记录的是右端点为x、长度为lowbit(x)的区间的区间和

    二维树状数组中,定义tree[x][y],记录的是右下角为(x,y),高为lowbit(x),宽为lowbit(y)的区间的区间和

    一、单点修改+区间查询

    //单点修改+区间查询
    void update(int x,int y,int z)//将点(x,y)加上z 
    {
        int mrk = y;
        while(x <= n)
        {
            y = mrk;
            while(y <= n)
            {
                tree[x][y] += z;
                y += lowbit(y);
            }
            x += lowbit(x);
        }
    }
    void query(int x,int y)//查询左上角为(1,1),右下角为(x,y)的矩阵和 
    {
        int ans = 0,mrk = y;
        while(x)
        {
            y = mrk;
            while(y)
            {
                ans += tree[x][y];
                y -= lowbit(y);
            }
            x -= lowbit(x);
        }
    }

    二、区间修改+单点查询

    “差分”思想

    由二维前缀和:

    sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + a[i][j]

    可得

    差分数组d[i][j] 为 a[i][j] 与 a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1]的差

    举个例子

    下面这个二位数组

    1   4   8
    6   7   2
    3   9   5

    对应的差分数组为

     1  3  4
     5 -2 -9
    -3  5  1

    给最中间的3*3矩阵加上x时

    差分数组的变化:

    0  0  0  0  0
    0 +x  0  0 -x
    0  0  0  0  0
    0  0  0  0  0
    0 -x  0  0 +x

    这样修改差分,效果为:

    0  0  0  0  0
    0  x  x  x  0
    0  x  x  x  0
    0  x  x  x  0
    0  0  0  0  0
    //二维树状数组:区间修改+单点查询
    void update(int x,int y,int z)
    {
        int mrk = y;
        while(x <= n)
        {
            y = mrk;
            while(y <= n)
            {
                tree[x][y] += z;
                y += lowbit(y);
            }
            x += lowbit(x); 
        }
    }
    
    void range_update(int xa,int ya,int xb,int yb,int z)
    {
        update(xa,ya,z);
        update(xa,yb+1,-z);
        update(xb+1,ya,-z);
        update(xb+1,yb+1,z);
    }
    
    void query(int x,int y)
    {
        int mrk = y,ans = 0;
        while(x)
        {
            y = mrk;
            while(y)
            {
                ans += tree[x][y];
                y -= lowbit(y);
            }
            x -= lowbit(x); 
        }
    }

    三、区间修改+区间查询

    //二维树状数组:区间修改+区间查询
    #include<cstdio>
    #include<algorithm>
    #define ll long long
    using namespace std;
    
    inline ll read()
    {
        ll sum = 0,p = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9')
        {
            if(ch == '-')
                p = -1;
            ch = getchar();
        }
        while(ch >= '0' && ch <= '9')
        {
            (sum *= 10) += ch - '0';
            ch = getchar();
        }
        return sum * p;
    }
    
    const int N =  205;
    ll n,m,Q;
    ll t1[N][N],t2[N][N],t3[N][N],t4[N][N];
    
    ll lowbit(ll o)
    {
        return o & -o;
    }
    
    void update(ll x,ll y,ll z)
    {
        for(int X = x;X <= n;X += lowbit(X))
            for(int Y = y;Y <= m;Y += lowbit(Y))
            {
                t1[X][Y] += z;
                t2[X][Y] += z * x;
                t3[X][Y] += z * y;
                t4[X][Y] += z * x * y;
            }
    }
    
    void range_update(ll xa,ll ya,ll xb,ll yb,ll z)
    {
        update(xa,ya,z);
        update(xa,yb + 1,-z);
        update(xb + 1,ya,-z);
        update(xb + 1,yb + 1,z);
    }
    
    ll query(ll x,ll y)
    {
        ll ans = 0;
        for(int i = x;i;i -= lowbit(i))
            for(int j = y;j;j -= lowbit(j))
                ans += (x + 1) * (y + 1) * t1[i][j] - (y + 1) * t2[i][j] - (x + 1) * t3[i][j] +t4[i][j];
        return ans;
    }
    
    ll range_query(ll xa,ll ya,ll xb,ll yb)
    {
        return query(xb, yb) - query(xb, ya - 1) - query(xa - 1, yb) + query(xa - 1, ya - 1);
    }
    
    int main()
    {
        n = read(),m = read(),Q = read();
        for(int i = 1;i <= n;i++)
            for(int j = 1;j <= m;j++)
            {
                ll z = read();
                range_update(i,j,i,j,z);
            }
        while(Q--)
        {
            ll ya = read(),xa = read(),yb = read(),xb = read(),z = read(),a = read();
            if(range_query(xa,ya,xb,yb) < z * (xb - xa + 1) * (yb - ya + 1))
                range_update(xa,ya,xb,yb,a);
        }
        for(int i = 1;i <= n;i++)
        {
            for(int j = 1;j <= m;j++)
                printf("%lld",range_query(i,j,i,j));
            printf("
    ");
        }
        return 0;
    } 
  • 相关阅读:
    系统程序员成长计划并发(二)(下)
    Web开发必知的八种隔离级别
    国产Android视频,Broncho A1
    Android中的BatteryService及相关组件
    Struts2输出XML格式的Result
    系统程序员成长计划并发(三)(上)
    入选”第一期中国最受欢迎50大技术博客”
    Broncho团队招聘应届毕业生(包括大四学生) 2名
    系统程序员成长计划并发(三)(下)
    WordPress MailUp插件‘Ajax’函数安全绕过漏洞
  • 原文地址:https://www.cnblogs.com/darlingroot/p/10737605.html
Copyright © 2020-2023  润新知