树状数组单点更新和区间更新,二维数组poj2155(区间更新,单点查询)(已加入区间修改区间查询)

普通的树状数组C[i]=a[i]+a[i-1]+...a[i-2^k+1]+...+a[1];

但是所有树状数组都是向上更新,向下求和。


1)、单点增减+区间求和

思路:C[x]表示该点的元素:sum(x)=C[1]+C[2]+……C[x]
[cpp]  view plain  copy
 print ?
  1. int arr[MAXN];  
  2. inline int sum(int x){int res=0;while(x)res+=arr[x],x-=lowbit(x);return res;}  
  3. inline void add(int x,int n){while(x
  4. inline int query(int x,int y){return sum(y)-sum(x-1);}  

(2)、区间增减+单点查询
思路:C[x]表示该点元素与左边元素的差值:num[x]=C[1]+C[2]+……C[x]
[cpp]  view plain  copy
 print ?
  1. int arr[MAXN]  
  2. inline int sum(int x){int res=0;while(x)res+=arr[x],x-=lowbit(x);return res;}  
  3. inline void add(int x,int n){while(x
  4. inline int update(int x,int y,int n){add(x,n);add(y+1,-n);}  


(3)、区间增减+区间查询
思路:C1[x]表示该点元素与左边的差值,C2[x]表示的是x*C[x]
[cpp]  view plain  copy
 print ?
  1. sum(sum(C[j],j<=i)i<=x)  
  2. = x*C[1]+(x-1)*C[2]+……+C[x]  
  3. =(x+1)*sum(C[i],i<=x)-sum(i*C[i],i<=x);  
  4. 树状数组的基本知识不再介绍,请自行百度

    我们假设sigma(r,i)表示r数组的前i项和,调用一次的复杂度是log2(i)

    设原数组是a[n],差分数组c[n],c[i]=a[i]-a[i-1],那么明显地a[i]=sigma(c,i),如果想要修改a[i]到a[j](比如+v),只需令c[i]+=v,c[j+1]-=v

    【今天的主要内容】

    我们可以实现NlogN时间的“单点修改,区间查询”,“区间修改,单点查询”,其实后者就是前者的一个变形,要明白树状数组的本质就是“单点修改,区间查询”

    怎么实现“区间修改,区间查询”呢?

    观察式子:
    a[1]+a[2]+...+a[n]

    = (c[1]) + (c[1]+c[2]) + ... + (c[1]+c[2]+...+c[n]) 

    = n*c[1] + (n-1)*c[2] +... +c[n]

    = n * (c[1]+c[2]+...+c[n]) - (0*c[1]+1*c[2]+...+(n-1)*c[n])    (式子①)

    那么我们就维护一个数组c2[n],其中c2[i] = (i-1)*c2[i]

    每当修改c的时候,就同步修改一下c2,这样复杂度就不会改变

    那么

    式子①

    =n*sigma(c,n) - sigma(c2,n)

    于是我们做到了在O(logN)的时间内完成一次区间和查询

    一件很好的事情就是树状数组的常数比NlogN的数据结构小得多,实际上它的计算次数比NlogN要小很多,再加上它代码短,一定会成为OI中的利器

    【题目】

    CodeVS 1082 线段树练习3(可以点击)

    解析:这就是个裸题

    【代码】

    [cpp]  view plain  copy
     print ?
    1. //树状数组(升级版)  
    2. #include   
    3. #define lowbit(x) (x&-x)  
    4. #define ll long long  
    5. #define maxn 200010  
    6. using namespace std;  
    7. ll n, q, c1[maxn], c2[maxn], num[maxn];  
    8. void add(ll *r, ll pos, ll v)  
    9. {for(;pos<=n;pos+=lowbit(pos))r[pos]+=v;}  
    10. ll sigma(ll *r, ll pos)  
    11. {  
    12.     ll ans;  
    13.     for(ans=0;pos;pos-=lowbit(pos))ans+=r[pos];  
    14.     return ans;  
    15. }  
    16. int main()  
    17. {  
    18.     ll i, j, type, a, b, v, sum1, sum2;  
    19.     scanf("%lld",&n);  
    20.     for(i=1;i<=n;i++)  
    21.     {  
    22.         scanf("%lld",num+i);  
    23.         add(c1,i,num[i]-num[i-1]);  
    24.         add(c2,i,(i-1)*(num[i]-num[i-1]));  
    25.     }  
    26.     scanf("%lld",&q);  
    27.     while(q--)  
    28.     {  
    29.         scanf("%lld",&type);  
    30.         if(type==1)  
    31.         {  
    32.             scanf("%lld%lld%lld",&a,&b,&v);  
    33.             add(c1,a,v);add(c1,b+1,-v);  
    34.             add(c2,a,v*(a-1));add(c2,b+1,-v*b);  
    35.         }  
    36.         if(type==2)  
    37.         {  
    38.             scanf("%lld%lld",&a,&b);  
    39.             sum1=(a-1)*sigma(c1,a-1)-sigma(c2,a-1);  
    40.             sum2=b*sigma(c1,b)-sigma(c2,b);  
    41.             printf("%lld\n",sum2-sum1);  
    42.         }  
    43.     }  
    44.     return 0;  
    45. }  



二维树状数组
//变换为0->1,1->0
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int MAXN = 1010;
int lowbit(int x)
{
    return x&(-x);
}
int c[MAXN][MAXN];
int n;
int sum(int x,int y)
{
    int ret = 0;
    for(int i = x;i > 0;i -= lowbit(i))
        for(int j = y;j > 0;j -= lowbit(j))
            ret += c[i][j];
    return ret;
}
void add(int x,int y,int val)
{
    for(int i = x;i <= n;i += lowbit(i))
        for(int j = y;j <= n;j += lowbit(j))
            c[i][j] += val;
}


int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int q;
        scanf("%d%d",&n,&q);
        memset(c,0,sizeof(c));
        char op[10];
        int x1,y1,x2,y2;
        while(q--)
        {
            scanf("%s",op);
            if(op[0] == 'C')
            {
                scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
//不要忘了是向上修改,所以x,y之前的区域修改是影响不到的。即只影响x,y为左上顶点的矩形区域
                add(x1,y1,1);
                add(x2+1,y1,1);
                add(x1,y2+1,1);
                add(x2+1,y2+1,1);//在(x1,y1)到(x2,y2)范围内加1,由于初始值为0,查询时只要那点结果是奇数则相当于奇数次变换,即结果位1
            }
            else
            {
                scanf("%d%d",&x1,&y1);
                if(sum(x1,y1)%2 == 0)printf("0\n");//在此题操作为区间修改吗,单点查询
                else printf("1\n");
            }
        }
        if(T > 0)printf("\n");
    }
    return 0;
}
/*
 * Author:  ktmzgl
 * Created Time:  2016/9/27 15:16:41
 * File Name: F:\Vim\code\Martix_BIT_poj2155.cpp
 */
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
const int maxint = -1u>>1;
const int N=1010;
int Scan()
{    //输入外挂  仅适用于纯数字,即不含符号的数字
    int res = 0, flag = 0;  
    char ch;  
    if((ch = getchar()) == '-') 
        flag = 1;  
    else if(ch >= '0' && ch <= '9') 
        res = ch - '0';  
    while((ch = getchar()) >= '0' && ch <= '9')  
        res = res * 10 + (ch - '0');  
    return flag ? -res : res;  
}  

void Out(int a) 
{    //输出外挂  
    if(a < 0) 
    {
        putchar('-');
        a = -a;
    }  
    if(a >= 10)
       Out(a / 10);  
    putchar(a % 10 + '0');  
}  
struct BIT
{
    int a[N][N];
    void init()
	{
		memset(a,0,sizeof(a));
	}
    void add(int x,int y,int t)
    {
        int i,j;
        for(i=x;i


你可能感兴趣的:(树状数组,acm)