已知一個數列,你須要進行下面兩種操做:ios
1.將某一個數加上x數組
2.求出某區間每個數的和數據結構
先來定義一個東西:lowbit字體
記lowbit(x)爲x的二進制最低位包含後面的0構成的數.spa
舉幾個栗子:code
8的二進制表示是1000,lowbit(8) =1000(2)= 8blog
6的二進制是110,lowbit(6) =10(2)= 2ip
求lowbitget
記f[i]是i的最低位.string
若i是奇數,f[i] = 1,不然f[i] = f[i/2] * 2.
是否是有點麻煩QwQ
其實能夠這樣:
int lowbit(int x) { return x&-x; }
因爲電腦一種叫作補碼的操做(因爲電腦是二進制,它們存的相反數是它的取反+1),一個數與它的相反數作與操做時會返回二進制下最右邊的1的位置。舉個例子: 6&-6=2 將6變成二進制:110。其中最右側的1加粗字體:110則返回的是二進制下10的值:
再來介紹一下樹狀數組
樹狀數組是一種用來求前綴和的數據結構.
對於原始數組A,咱們設一個數組C
C[i]=A[i-lowbit(i)+1]+...+A[i]
i>0的時候C[i]纔有用,C就是樹狀數組
大概就是這樣:
樹狀數組用於解決單個元素常常修改,並且還反覆求不一樣區間和的狀況
樹狀數組求和
樹狀數組只可以支持詢問前綴和,不支持區間和,可是能夠用前綴和求區間和.
咱們先找到C[n],而後咱們發現如今,下一個要找的點是n − lowbit(n),而後咱們不斷的減去lowbit(n)並累加C數組.
咱們能夠用前綴和相減的方式來求區間和.
詢問的次數和n的二進制裏1的個數相同,時間複雜度是O(log N).
代碼:
int ask(int x) //查詢[1,x]的值 { int ans=0; for (;x;x-=lowbit(x)) ans+=s[x]; //咱們詢問的]是[x-lowbit(x)+1,x,而後後面一個區間是以x-lowbit(x)爲右端點的,依次類推 return ans; }
樹狀數組更新
如今咱們要修改Ax的權值,考慮全部包含x這個位置的區間個數.
從C[x]開始,下一個應該是C[y = x + lowbit(x)],再下一個是C[z = y + lowbit(y)]...直到達到上限
注意到每一次更新以後,位置的最低位1都會往前提1.總複雜度也爲O(log N).
大概就是這樣:
代碼:
void ins(int x,int y)//修改a[x]的值,a[x]+=y; { for (;x<=n;x+=lowbit(x)) s[x]+=y; //首先須要修改的區間是以x爲右端點的,而後下一個區間是x+lowbit(x),以此類推 }
因此這一個題的代碼就出來了:
#include<cstdio> #include<iostream> #include<cstdlib> #include<iomanip> #include<cmath> #include<cstring> #include<string> #include<algorithm> #include<time.h> #include<queue> using namespace std; typedef long long ll; typedef long double ld; typedef pair<int,int> pr; const double pi=acos(-1); #define rep(i,a,n) for(int i=a;i<=n;i++) #define per(i,n,a) for(int i=n;i>=a;i--) #define Rep(i,u) for(int i=head[u];i;i=Next[i]) #define clr(a) memset(a,0,sizeof a) #define pb push_back #define mp make_pair #define fi first #define sc second ld eps=1e-9; ll pp=1000000007; ll mo(ll a,ll pp){if(a>=0 && a<pp)return a;a%=pp;if(a<0)a+=pp;return a;} ll powmod(ll a,ll b,ll pp){ll ans=1;for(;b;b>>=1,a=mo(a*a,pp))if(b&1)ans=mo(ans*a,pp);return ans;} ll read(){ ll ans=0; char last=' ',ch=getchar(); while(ch<'0' || ch>'9')last=ch,ch=getchar(); while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar(); if(last=='-')ans=-ans; return ans; } //head using namespace std; int m,n,s[5000005]; long long ans; int lowbit(int x) { return x&-x; } void ins(int x,int y)//修改a[x]的值,a[x]+=y; { for (;x<=n;x+=lowbit(x)) s[x]+=y; //首先須要修改的區間是以x爲右端點的,而後下一個區間是x+lowbit(x),以此類推 } int ask(int x) //查詢[1,x]的值 { int ans=0; for (;x;x-=lowbit(x)) ans+=s[x]; //咱們詢問的]是[x-lowbit(x)+1,x,而後後面一個區間是以x-lowbit(x)爲右端點的,依次類推 return ans; } int main() { n=read(),m=read(); rep(i,1,n) { int t=read(); ins(i,t); } rep(i,1,m) { int a,b,c; a=read(),b=read(),c=read(); if(a==1) { ins(b,c); } if(a==2) { printf("%d\n",ask(c)-ask(b-1));//區間查詢則爲右邊界前綴和減去左邊界前綴和 } } }
已知一個數列,你須要進行下面兩種操做:
1.將某區間每個數加上x
2.求出某一個數的值
若是將x到y區間加上一個k,那就是從x到n都加上一個k,再從y+1到n加上一個-k
加的移動仍是i+=lowbit(i);
或者說叫差分
差分:
設數組a[]={1,6,8,5,10},那麼差分數組b[]={1,5,2,-3,5}
也就是說b[i]=a[i]-a[i-1];(a[0]=0;),那麼a[i]=b[1]+....+b[i];(這個很好證的)。
假如區間[2,4]都加上2的話
a數組變爲a[]={1,8,10,7,10},b數組變爲b={1,7,2,-3,3};
發現了沒有,b數組只有b[2]和b[5]變了,由於區間[2,4]是同時加上2的,因此在區間內b[i]-b[i-1]是不變的.
因此對區間[x,y]進行修改,只用修改b[x]與b[y+1]:
b[x]=b[x]+k;b[y+1]=b[y+1]-k;
就像這樣
ins(b,d); ins(c+1,-d);
樹狀數組單點查詢
int ask(int x) { int ans=0; for (;x;x-=lowbit(x)) ans+=s[x]; return ans; }
因此最後的代碼是這樣的:
#include<cstdio> #include<iostream> #include<cstdlib> #include<iomanip> #include<cmath> #include<cstring> #include<string> #include<algorithm> #include<time.h> #include<queue> using namespace std; typedef long long ll; typedef long double ld; typedef pair<int,int> pr; const double pi=acos(-1); #define rep(i,a,n) for(int i=a;i<=n;i++) #define per(i,n,a) for(int i=n;i>=a;i--) #define Rep(i,u) for(int i=head[u];i;i=Next[i]) #define clr(a) memset(a,0,sizeof a) #define pb push_back #define mp make_pair #define fi first #define sc second ld eps=1e-9; ll pp=1000000007; ll mo(ll a,ll pp){if(a>=0 && a<pp)return a;a%=pp;if(a<0)a+=pp;return a;} ll powmod(ll a,ll b,ll pp){ll ans=1;for(;b;b>>=1,a=mo(a*a,pp))if(b&1)ans=mo(ans*a,pp);return ans;} ll read(){ ll ans=0; char last=' ',ch=getchar(); while(ch<'0' || ch>'9')last=ch,ch=getchar(); while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar(); if(last=='-')ans=-ans; return ans; } //head using namespace std; int m,n,t[5000005],s[5000005]; long long ans; int lowbit(int x) { return x&-x; } void ins(int x,int y) { for (;x<=n;x+=lowbit(x)) s[x]+=y; } int ask(int x) { int ans=0; for (;x;x-=lowbit(x)) ans+=s[x]; return ans; } int main() { n=read(),m=read(); rep(i,1,n) { t[i]=read(); } rep(i,1,m) { int a; a=read(); if(a==1) { int b=read(),c=read(),d=read(); ins(b,d); ins(c+1,-d); } if(a==2) { int b=read(); printf("%d\n",t[b]+ask(b)); } } }
PS.這裏s數組存的是原數組t每個元素增長或減小的多少