ツリー配列のまとめ-二次元区間クエリー、区間修正差分式の導出

54509 ワード

ツリー配列個人まとめ
単点修正、区間照会
int tree[maxn];
inline int lowbit(int x){
    return x&(-x);
}
inline void update(int x,int val){ // x     val
    for(int i=x;i<maxn;i+=lowbit(i)){
        tree[i]+=val;
    }
}
inline int query(int x){ // [1,x]      
    int ans=0;
    for(int i=x;i>0;i-=lowbit(i)){
        ans+=tree[i];
    }
    return ans;
}
単点修正、区間最値
注意ある位置xの値を修正する場合も、a[x]の値を修正します.
int tree[maxn],a[maxn];
inline int lowbit(int x){
    return x&(-x);
}
inline void update(int x,int val,int n){
    for(int i=x;i<=n;i+=lowbit(i)){
        tree[i]=val;
        for(int j=1;j<lowbit(i);j<<=1){
            tree[i]=max(tree[i],tree[i-j]);
        }
    }
}
inline int query(int x,int y){
    int ans=0;
    while(x<=y){
        ans=max(ans,a[y]);
        for(y-=1;y-lowbit(y)>=x;y-=lowbit(y)){
            ans=max(ans,tree[y]);
        }
    }
    return ans;
}
k番目の小さい値を求めます
  :

​	   a  ,a[i]=1  ,     i   ,             k 

          ,    k  。     O(n)      

​	               ,              ,     。

    :236810,  k=4   

        a 1 2 3 4 5 6 7 8 9 100 1 1 0 0 1 0 1 0 1

ans         ,cnt            ,  cnt   。

t=log2(10)=3;

t=3 ,ans=0+2^3=8<10,cnt+tree[8]=a[1]+a[2]+……+a[8]=4==4,  ans-=8=0;

t=2 ,ans=0+2^2=4<10,cnt+tree[4]=0+a[1]+a[2]+a[3]+a[4]=2<k,  cnt+=2=2;

t=1 ,ans=4+2^1=6<10,cnt+tree[6]=2+a[5]+a[6]=3<k,  cnt+=1=3;

t=0 ,ans=6+1=7<10,cnt+tree[7]=3+a[7]=3<k,  cnt+=0=34    ans+1=8.
int tree[maxn];
inline int lowbit(int x){
    return x&(-x);
}
inline void update(int x,int val){ // x     val,  val   1
    for(int i=x;i<maxn;i+=lowbit(i)){
        tree[i]+=val;
    }
}
inline int query(int x){ // [1,x]      
    int ans=0;
    for(int i=x;i>0;i-=lowbit(i)){
        ans==tree[i];
    }
    return ans;
}
inline get_kth(int k,int n){ //  k     ,n       ,         1
    int ans=0,cnt=0; //ans       ,cnt         
    for(int i=log2(n);i>=0;i--){
        ans+=1<<i;
        if(ans>=n||cnt+tree[ans]>=k) ans-=1<<i; //          ,      k,  
        else cnt+=tree[ans];
    }
    return ans+1;
}
区間修正、単体検索
考え方:
差分を利用する思想、すなわち、a k=Σi=1 k d iであり、d i=a i−a i−1 a_k=\sum_{i=1}^kd_i,そのうちd_i=a_i-a_{i−1}ak=i=1Σk diでは、di=ai−ai−1は上式である位置の値が始点の値の大きさだけに関係していることが分かりますので、区間を変更する場合は、端点の値だけを修正すれば良いです.
int tree[maxn];
inline int lowbit(int x){
    return x&(-x);
}
inline void update(int x,int val){
    for(int i=x;i<maxn;i+=lowbit(i)){
        tree[i]+=val;
    }
}
inline int query(int x){
	int ans=0;
    for(int i=x;i>0;i-=lowbit(i)){
        ans+=tree[i];
    }
    return ans;
}
int a[maxn];
int main(){
    int n;scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",a+i),update(i,a[i]-a[i-1]); //   
   	update(2,10);update(8,-10); //   [2,7]        10
   	printf("%d
%,query(4)); // [4] }
区間変更、区間照会
考え方:
また、差分を利用する思想でもある.a kについてはa k=Σi=1 k i=1 k i=Σi=1 k i=Σi=1 kΣi=1 kΣj=1 i=Σi==1 i===1 i===Σi=1 i=1 i=1 k a i=Σi=1 i=1 k=1 i=1 k=1 k=1 i=1 i=1 i i==1 i=i=1 i=1 i==i===1 i i i i=====1 i i i======1 i==================1 i i i i i i i i i i i=============a_に対してk私たちはa_を知っていますk=\sum_{i=1}^kd_i\則\sum_{i=1}^カ_i=\sum_{i=1}^k\sum_{j=1}^id_j=\sum_{i=1}^k(k+1-i)\cdot d_i\なら\sum_が得られます{i=1}^カ_i=\sum_{i=1}^k(k+1)\cdot d_i-\sum_{i=1}^ki\cdot d_i=(k+1)\cdot\sum_{i=1}^kd_i-\sum_{i=1}^ki\cdot d_iはakについて私たちは、ak=i=1Σk di則i=1Σk ai=i=i=1Σk j=1Σi dj=i=i=1Σi=1Σk(k+1+1+1)dia配列Σ(p=1)di=1=1Σi=1(k+1)dididi i=1=1 di i=1=1 dii=1=1 didia a a a a a a a=1=1=1=1=1=1=1=1==1 dididididi 1=1===1=1===========1 dididi 1=1===1 di n n n n n n n n n n n nつの配列はそれぞれd_i_uを記憶する.i diとi⋅d i\cdot d_i i⋅diは、区間を更新するときは上と同じようにエンドポイントだけを更新すればいいです.
修正する区間が[x,y]であれば、update(x,val);update(y+1,-val);に更新されます.
照会する区間が[x,y]であれば、照会はquery(y)-query(x-1);です.
int tree1[maxn],tree2[maxn];
inline int lowbit(int x){
    return x&(-x);
}
inline void update(int x,int val){
    for(int i=x;i<maxn;i+=lowbit(i)){
        tree1[i]+=val;tree2[i]+=x*val;
    }
}
inline int query(int x){
    int ans=0;
    for(int i=x;i>0;i-=lowbit(i)){
        ans+=(x+1)*tree1[i]-tree2[i];
    }
    return ans;
}
int a[maxn];
int main(){
    int n;scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",a+i),update(i,a[i]-a[i-1]); //   
    update(2,10);update(8,-10); //   [2,7]      10
    printf("%d
"
,query(6)-query(4-1)); // [4,6] }
高次元樹状配列
その他に、簡単に修正すればいいです.最後のインターバルクエリについて、区間修正は簡単に次のように導出されます.a n m=Σi=1 nΣj=1 m d i jで、d i j=a i j−a(i−1)j˙ −a i(˙ j−1)+a(i−1)(j−−1)は、Σi=1 nΣj=1 m a i i=Σi=Σi=1 nΣi=1 nΣj=1 nΣj=1 i=1Σp=Σi=1 nΣj=1 m(m?nn n−−−−−−−−−−−−−i(i−i−−1)0 0 0 0 0 0 0 0 0 0 0 0 0 m=1)0 0 0 0 m=1=1==1===1===1 n n n n n n n n n n n n n n==========================j=1 m a i j=(m⋅n)⋅Σi=1 nΣj=1 m d i−i−mΣi=1 nΣj=1 m(i−1)⋅di−nΣi=1 m(j−1)_i=1 m(j−1)⋅dj+Σi=1 i{nm}=\sum_{i=1}^n\sum_{j=1}^md_{ij}の中で、d_{ij}=a_{ij}-a_{(i-1)\dot j}-a_{i\dot(j-1)}+a_{(i-1)(j-1)}\則\sum_{i=1}^n\sum_{j=1}ma_{ij}=\sum_{i=1}^n\sum_{j=1}^m\sum_{k=1}^i\sum_{p=1}^jd_{kp}=\sum_{i=1}^n\sum_{j=1}^m(m\cdot n-(i-1)\cdot m-(j-1)\cdot n+(i-1)\cdot(j-1))\cdot d_{ij}\なら、\sum_が得られます.{i=1}^n\sum_{j=1}ma_{ij}=\(m\cdot n)\cdot\sum_{i=1}^n\sum_{j=1}^md_{ij}-m\cdot\sum_{i=1}^n\sum_{j=1}^m(i-1)\cdot d_{ij}-n\cdot\sum_{i=1}^n\sum_{j=1}^m(j-1)\cdot d_{ij}+\sum_{i=1}^n\sum_{j=1}^m(i-1)\cdot(j-1)\cdot d_{i j}anm=i=1Σn j=1Σm dijのため、dij=aij−a(i−1)j˙−ai(˙j−1)+a(i−1)(j−−1)、i=1Σn j=1Σm aij=1Σm aij=i=1Σn n j=1Σn n j=1Σm=1Σp=1Σj=1Σn n j=1Σm(m?nn−−−−−−−−−−−−i(i−i−1)0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 m==1=1=1=1=1=1=============1=1============1=1=====1 i=1Σn j=1Σm dij−m_i=1Σn j=1Σm dij=1Σn j=1Σn j=1Σn j=1Σm(i−1)⋅dij−n_i=1Σn j=1Σm(j−1)⋅di j+i=1Σn j=1Σm(i−1)⋅(j−1)⋅dijは、4つの配列でそれぞれd i j d_を維持する必要があります.{i j}dij,(i−1)⋅di j(i−1)\cdot d_{i j}(i−1)⋅dij,(j−1)⋅di j(j−1)\cdot d_{i j}(j−1)⋅dijと(i−1)⋅(j−1)⋅di j(i−1)\cdot(j−1)\cdot d_{i j}(i−1)⋅(j−1)⋅dij
修正する矩形区間が[x 1,y 1]-[x 2,y 2]の場合、更新するポイントはupdate(x1,y1,val);update(x2+1,y2+1,val);update(x1,y2+1,-val);update(x2+1,y1,-val);クエリーの実行区間が[x 1,y 1]-[x 2,y 2]の場合、クエリーの値はquery(x2,y2)-query(x1-1,y2)-query(x2,y1-1)+query(x1-1,y1-1)
int tree1[maxn][maxn],tree2[maxn][maxn],tree3[maxn][maxn],tree4[maxn][maxn];
inline int lowbit(int x){
    return x&(-x);
}
inline void update(int x,int y,int val){
    for(int i=x;i<maxn;i+=lowbit(i)){
        for(int j=y;j<maxn;j+=lowbit(j)){
            tree1[i][j]+=val;tree2[i][j]+=(x-1)*val;
            tree3[i][j]+=(y-1)*val;tree4[i][j]+=(x-1)*(y-1)*val;
        }
    }
}
inline int query(int x,int y){
    int ans=0;
    for(int i=x;i>0;i-=lowbit(i)){
        for(int j=y;j>0;j-=lowbit(j)){
            ans+=(x*y)*tree1[i][j]-y*tree2[i][j]-x*tree3[i][j]+tree4[i][j];
        }
    }
    return ans;
}
int a[maxn][maxn];
int main(){
    int n,m;scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            scanf("%d",&a[i][j]);
            update(i,j,a[i][j]-a[i-1][j]-a[i][j-1]+a[i-1][j-1]);
        }
    }
    update(2,3,10);update(7,6,10);update(2,6,-10);update(7,3,-10); 
    //     [2,3]-[6,5]      10
    printf("%d
"
,query(4,5)+query(2,1)-query(2,5)-query(4,2)); // [3,2]-[4,5] }