树状数组

Posted ganster

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了树状数组相关的知识,希望对你有一定的参考价值。

树状数组总结

        首先我们来说一下这个算法的正确性,从一维开始吧,我们假设有两个点,i,i1,i一直加上lowbit(i),而i1一直减去lowbit(i1),这是两个严格单调的序列,如果这两个序列有交点,则有且只有一个交点k,则从i~i1这一段的和可以用c[k]来记录(且只有一个c[k]满足)。

        二维树状数组也是类似,首先明确二维的c[i][j]记录的是以( i,j  )为右下,( i-lowbit(i)+1 ,j-lowbit(j)+1   )为左上的矩形中所含元素的和(  至于为什么不能理解为一排一排的一维树状数组,是因为这样时间复杂度会从(logn)^2变为nlogn),假设有一个点(  i1 , j1 ),i 与 i1 可以确定好交点的横坐标, 而 j 与 j1 可以确定该交点宗坐标,则交点确定。

例题

                                                          Problem 1. matsum
                                                                                                               Input file: matsum.in
                                                                                                            Output file: matsum.out
                                                                                                               Time limit: 1 second
给出N M 的一个矩阵,初始时全部元素为0,有两种操作:
? modify x y d:将位置(x,y) 的值加上d。
? query x1 y1 x2 y2:询问(x1,y1)-(x2,y2) 这个矩形的和。1
Input
第1 行,3 个整数N;M;Q,分别表示矩阵的行数、列数及询问数。
接下来Q 行,每行是上面两种操作之一。
Output
对于每个询问,输出询问结果。
Sample
matsum.in matsum.out
2 2 4
modify 1 1 4
query 1 1 2 2
modify 2 2 -4
query 1 1 2 2
40
Note
? 对于30% 的数据,1  N;M;Q  500;
? 对于另外30% 的数据,N = 1; 1  M;Q  105;
? 对于另外40% 的数据,1  N;M  103; 1  Q  105;
? 对于所有数据,jdj  100; 1  x  N; 1  y  M; 1  x1  x2  N; 1  y1  y2  M ;

#include <cstdio>

int n, m, q;

struct Case1 {
    int bit[100010];
    void modify( int p, int d ) {
        for( int i = p; i <= m; i += i & -i )
            bit[i] += d;
    }
    int query( int r ) {
        int rt = 0;
        for( int i = r; i; i -= i & -i ) 
            rt += bit[i];
        return rt;
    }
    int query( int l, int r ) {
        return query(r) - query(l-1);
    }
    void solve() {
        while (q--) {
            char ss[100];
            scanf( "%s", ss );
            if( ss[0] == m ) {
                int x, y, d;
                scanf( "%d%d%d", &x, &y, &d );
                modify( y, d );
            } else {
                int x1, y1, x2, y2;
                scanf( "%d%d%d%d", &x1, &y1, &x2, &y2 );
                printf( "%d\n", query(y1,y2) );
            }
        }
    }
}case1;
struct Case2 {
    int bit[1100][1100];
    void modify( int x, int y, int d ) {
        for( int i = x; i <= n; i += i & -i )
            for( int j = y; j <= m; j += j & -j )
                bit[i][j] += d;
    }
    int query( int x, int y ) {
        int rt = 0;
        for( int i = x; i; i -= i & -i )
            for( int j = y; j; j -= j & -j )
                rt += bit[i][j];
        return rt;
    }
    int query( int x1, int y1, int x2, int y2 ) {
        return query(x2,y2) - query(x1-1,y2) - query(x2,y1-1) + query(x1-1,y1-1);
    }
    void solve() {
        while(q--) {
            char ss[100];
            scanf( "%s", ss );
            if( ss[0] == m ) {
                int x, y, d;
                scanf( "%d%d%d", &x, &y, &d );
                modify( x, y, d );
            } else {
                int x1, y1, x2, y2;
                scanf( "%d%d%d%d", &x1, &y1, &x2, &y2 );
                printf( "%d\n", query(x1,y1,x2,y2) );
            }
        }
    }
}case2;

int main() {
    freopen ( "matsum.in", "r", stdin ) ;
    freopen ( "matsum.out", "w", stdout ) ;
    scanf( "%d%d%d", &n, &m, &q );
    if( n == 1 )
        case1.solve();
    else
        case2.solve();
}

 

以上是关于树状数组的主要内容,如果未能解决你的问题,请参考以下文章

数据结构之树状数组从零认识树状数组

树状数组和线段树有啥区别?

树状数组

树状数组

树状数组

如何利用树状数组修改一个区间?