樹狀陣列單點更新和區間更新,二維陣列poj2155(區間更新,單點查詢)(已加入區間修改區間查詢)
阿新 • • 發佈:2018-12-24
普通的樹狀陣列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?
- int arr[MAXN];
- inlineint sum(int x){int res=0;while(x)res+=arr[x],x-=lowbit(x);return res;}
-
inlinevoid add(int x,int n){while(x<MAXN)arr[x]+=n,x+=lowbit(x);}
- 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?
- int arr[MAXN]
- inlineint sum(int x){int res=0;while(x)res+=arr[x],x-=lowbit(x);return res;}
-
inlinevoid add(int x,int n){while(x<MAXN)arr[x]+=n,x+=lowbit(x);}
- 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?
- sum(sum(C[j],j<=i)i<=x)
- = x*C[1]+(x-1)*C[2]+……+C[x]
- =(x+1)*sum(C[i],i<=x)-sum(i*C[i],i<=x);
-
樹狀陣列的基本知識不再介紹,請自行百度
我們假設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?- //樹狀陣列(升級版)
- #include <cstdio>
- #define lowbit(x) (x&-x)
- #define ll long long
- #define maxn 200010
- usingnamespace std;
- ll n, q, c1[maxn], c2[maxn], num[maxn];
- void add(ll *r, ll pos, ll v)
- {for(;pos<=n;pos+=lowbit(pos))r[pos]+=v;}
- ll sigma(ll *r, ll pos)
- {
- ll ans;
- for(ans=0;pos;pos-=lowbit(pos))ans+=r[pos];
- return ans;
- }
- int main()
- {
- ll i, j, type, a, b, v, sum1, sum2;
- scanf("%lld",&n);
- for(i=1;i<=n;i++)
- {
- scanf("%lld",num+i);
- add(c1,i,num[i]-num[i-1]);
- add(c2,i,(i-1)*(num[i]-num[i-1]));
- }
- scanf("%lld",&q);
- while(q--)
- {
- scanf("%lld",&type);
- if(type==1)
- {
- scanf("%lld%lld%lld",&a,&b,&v);
- add(c1,a,v);add(c1,b+1,-v);
- add(c2,a,v*(a-1));add(c2,b+1,-v*b);
- }
- if(type==2)
- {
- scanf("%lld%lld",&a,&b);
- sum1=(a-1)*sigma(c1,a-1)-sigma(c2,a-1);
- sum2=b*sigma(c1,b)-sigma(c2,b);
- printf("%lld\n",sum2-sum1);
- }
- }
- return 0;
- }
#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;
}