1. 程式人生 > >樹狀陣列單點更新和區間更新,二維陣列poj2155(區間更新,單點查詢)(已加入區間修改區間查詢)

樹狀陣列單點更新和區間更新,二維陣列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. inlineint sum(int x){int res=0;while(x)res+=arr[x],x-=lowbit(x);return res;}  
  3. inlinevoid add(int x,int n){while(x<MAXN)arr[x]+=n,x+=lowbit(x);}  
  4. inlineint 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. inlineint sum(int x){int res=0;while(x)res+=arr[x],x-=lowbit(x);return res;}  
  3. inlinevoid add(int x,int n){while(x<MAXN)arr[x]+=n,x+=lowbit(x);}  
  4. inlineint 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中的利器

    【題目】

    解析:這就是個裸題

    【程式碼】

    [cpp] view plain copy  print?在CODE上檢視程式碼片派生到我的程式碼片
    1. //樹狀陣列(升級版)
    2. #include <cstdio>
    3. #define lowbit(x) (x&-x)
    4. #define ll long long
    5. #define maxn 200010
    6. usingnamespace 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 <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <time.h>
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 <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <time.h>
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<N;i+=i&-i)
        {
            for(j=y;j<N;j+=j&-j) a[i][j]+=t;
        }
    }
     
    int get(int x,int y)
    {
        int ans=0;
        int i,j;
        for(i=x;i;i-=i&-i)
        {
            for(j=y;j;j-=j&-j) ans+=a[i][j];
        }
        return ans;
    }
};
 
BIT a,b,c,d;
 
 
void add(int x1,int y1,int x2,int y2,int t)
{
    a.add(x1,y1,t); a.add(x2+1,y1,-t);
    a.add(x1,y2+1,-t); a.add(x2+1,y2+1,t);
     
    b.add(x1,y1,t*x1); b.add(x2+1,y1,-t*(x2+1));
    b.add(x1,y2+1,-t*x1); b.add(x2+1,y2+1,t*(x2+1));
     
    c.add(x1,y1,t*y1); c.add(x2+1,y1,-t*y1);
    c.add(x1,y2+1,-t*(y2+1)); c.add(x2+1,y2+1,t*(y2+1));
     
    d.add(x1,y1,t*x1*y1); d.add(x2+1,y1,-t*(x2+1)*y1);
    d.add(x1,y2+1,-t*x1*(y2+1)); d.add(x2+1,y2+1,t*(x2+1)*(y2+1));
}
 //區間修改區間求和的時候樹狀陣列存的是差量 
int get(int x,int y)//求1,1到x,y的和
{
    return (x+1)*(y+1)*a.get(x,y)-(y+1)*b.get(x,y)-(x+1)*c.get(x,y)+d.get(x,y);
}
 
int get(int x1,int y1,int x2,int y2)//求x1,y1到x2,y2的和
{
    return get(x2,y2)-get(x2,y1-1)-get(x1-1,y2)+get(x1-1,y1-1);
}
int main()
{
	int t;
	scanf("%d",&t);
	int cnt=0;
	while(t--)
	{
		if(cnt!=0)
		cout<<endl;
		cnt++;
		int n,m;
		scanf("%d%d",&n,&m);
		a.init(),b.init(),c.init(),d.init();
		char op[10];
		int x1,y1,x2,y2;
		for(int i=0;i<m;i++)
		{
			scanf("%s",op);
			if(op[0]=='C')
			{
				//scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
				x1=Scan(),y1=Scan(),x2=Scan(),y2=Scan();
				add(x1,y1,x2,y2,1);
			}
			else
			{
				//scanf("%d%d",&x1,&y1);
				x1=Scan(),y1=Scan();
				cout<<get(x1,y1,x1,y1)%2<<endl;
			}
		}
		
	}
    return 0;
}