樹狀陣列 區間修改 區間查詢 講解
原來的值存在a[]裡面,多建立個數組c1[],注意:c1[i]=a[i]-a[i-1]。
那麼求a[i]的值的時候:
a[i]=a[i-1]+c1[i]=a[i-2]+c1[i]+c1[i-1]=…..=c1[1]+c1[2]+…+c1[i]。
我們叫c1[]陣列為差分陣列。
這樣之後,a[i]就可以用差分陣列的區間和來表示。之後我們就不需要原陣列a[i]了,只需要維護c1[i]就可以。
這樣我們區間修改時,對於差分陣列來說隻影響端點處的值。也就是對於[l,r]增量k時,只需要讓c1[l]+=k,c1[r+1]-=k即可。
#include<bits/stdc++.h> using namespace std; #include<cstring> #define lowbit(x) (x&-x) int const maxn=1e5+10; int c[maxn];//樹 int n; int sum(int i){ int s=0; while(i>0){ s+=c[i]; i-=lowbit(i); } return s; } void add(int i,int val){ while(i<=n){ c[i]+=val; i+=lowbit(i); } } int main(){ int x; cin>>n; for(int i=1;i<=n;i++){ scanf("%d",&x); add(i,x); add(i+1,-x); } int l,r,q; cin>>q; for(int i=1;i<=q;i++){ cin>>x; cout<<sum(x)<<endl; } }
區間查詢:
我們用sum(1,k)表示區間1到k的和。
那麼
sum(1,k) = c1(1)+
c1(1)+c1(2)+
c1(1)+c1(2)+c1(3)+
…+
c1(1)+c1(2)+…+c1(k)。
= k*c1(1) + (k-1)*c1(2) + ... +1 * c1(k)
= k*( c1[1] + c1[2] +...+c1[k] ) - ( 0*c1[1] + 1 *c1[2] + ... +(k-1) * c1[k] )
= k*sum(c1,k) - ( 0*c1[1] + 1 *c1[2] + ... +(k-1) * c1[k] )
減號後面的需要額外維護c2陣列:c2[i]=(i-1)*c2[i] 然後每次求字首和即可。
區間查詢:
#include<bits/stdc++.h> using namespace std; #include<cstring> #define lowbit(x) (x&-x) int const maxn=1e5+10; int c[maxn],c2[maxn];//樹 int n; int sum(int c[],int i){ int s=0; while(i>0){ s+=c[i]; i-=lowbit(i); } return s; } void _add2(int c[],int i,int val){ while(i<=n){ c[i]+=val; i+=lowbit(i); } } void add(int c[],int i,int val){ _add2(c2,i,val*(i-1)); while(i<=n){ c[i]+=val; i+=lowbit(i); } } int sum2(int r){ return r*sum(c,r) - sum(c2,r); } int main(){ // freopen("r.txt","r",stdin); int x; cin>>n; int l,r,q; for(int i=1;i<=n;i++){ scanf("%d%d%d",&l,&r,&x); add(c,l,x); add(c,r+1,-x); } cin>>q; for(int i=1;i<=q;i++){ cin>>l>>r; cout<<sum2(r)-sum2(l-1)<<endl; } }