淺談二維線段樹


一、定義

二維線段樹,即用線段樹維護一個矩陣

有兩種實現方式:

1、原一維線段樹的基礎上,每一個節點都是一個線段樹,代表第二維

下圖是一個4*4矩陣

 

2、四分法轉化為一維線段樹

 

兩種方法的空間復雜度都是n*n*log^2

第一種方法單次操作的時間復雜度是log^2,第二種方法最差可以退化到n

一維線段樹的標記思想,在第一種方法中,可以用於二維線段樹的第二維,不可以用於二維線段樹的第一維

第二種方法本質上是四叉的一維線段樹,

在此只介紹第一種方法

二、基本操作

1、單點修改+矩陣查詢

單次訪問一個位置,查詢一個矩陣的總訪問次數

訪問位置(x,y)

所有第一維包含x的都包含位置(x,y),訪問次數都要加1

所以從根到找到第一維包含x所經過的所有節點,在里面找到第二維包含y的節點,訪問次數加1

sum[i][j] 表示的是這個節點所包含的矩陣的所有位置的訪問次數之和

第一維:

void changex(int kx,int l,int r)
{
    changey(kx,1,1,h);
    if(l==r) return;
    int mid=l+r>>1;
    if(x<=mid) changex(kx<<1,l,mid);
    else changex(kx<<1|1,mid+1,r);
}

對於第二維的修改有兩種寫法

①、從根往下找經過的節點 f訪問次數全部+1

因為此時已經確定了第一維包含,從根往下找y,所經過的區間一定包含y

 

void changey(int kx,int ky,int l,int r)
{
    sum[kx][ky]++;
    if(l==r) return;
    int mid=l+r>>1;
    if(y<=mid) changey(kx,ky<<1,l,mid);
    else changey(kx,ky<<1|1,mid+1,r);
}

②、在第二維中找到y,訪問次數+1,祖先節點通過左右子節點的合並修改

void changey(int kx,int ky,int l,int r)
{
    if(l==r)
    {
        sum[kx][ky]++;
        return;
    }
    int mid=l+r>>1;
    if(y<=mid) changey(kx,ky<<1,l,mid);
    else changey(kx,ky<<1|1,mid+1,r);
    sum[kx][ky]=sum[kx][ky<<1]+sum[kx][ky<<1|1];
}

 

查詢:

查詢左上角為(xl,yl),右下角為(xr,yr)的矩陣的所有位置的訪問次數之和

void queryy(int kx,int ky,int l,int r)
{
    if(l>=yl && r<=yr) { cnt+=sum[kx][ky]; return; } int mid=l+r>>1; if(yl<=mid) queryy(kx,ky<<1,l,mid); if(yr>mid) queryy(kx,ky<<1|1,mid+1,r); } void queryx(int kx,int l,int r) { if(l>=xl && r<=xr) { queryy(kx,1,1,h); return; } int mid=l+r>>1; if(xl<=mid) queryx(kx<<1,l,mid); if(xr>mid) queryx(kx<<1|1,mid+1,r); }

 

 

2、矩陣修改+單點查詢

修改

訪問左上角為(x1,y1),右下角為(x2,y2)的矩陣

sum[i][j] 表示的是這個節點所代表的矩陣的整體訪問次數

即這個矩陣的每一個位置都會有一個sum[i][j]的訪問次數

void changey(int kx,int ky,int l,int r)
{
    if(y1<=l&&r<=y2)
    {
        sum[kx][ky]++;
        return;
    }
    int mid=l+r>>1;
    if(y1<=mid) changey(kx,ky<<1,l,mid);
    if(y2>mid) changey(kx,ky<<1|1,mid+1,r);
}
void changex(int kx,int l,int r)
{
    if(x1<=l&&r<=x2)
    {
        changey(kx,1,1,n);
        return;
    }
    int mid=l+r>>1;
    if(x1<=mid) changex(kx<<1,l,mid);
    if(x2>mid) changex(kx<<1|1,mid+1,r);
}

 

查詢

查詢位置(x,y)的訪問次數

因為sum[i][j] 表示的是一個矩陣的整體訪問次數,所以找(x,y)經過的所有點都要累計其訪問次數

void queryy(int kx,int ky,int l,int r)
{
    ans+=sum[kx][ky];
    if(l==r) return;
    int mid=ly+ry>>1;
    if(y<=mid) queryy(kx,ky<<1,l,mid);
    else queryy(kx,ky<<1|1,mid+1,r);
}
void queryx(int kx,int l,int r)
{
    queryy(kx,1,1,n);
    if(l==r) return;
    int mid=l+r>>1;
    if(x<=mid) queryx(kx<<1,l,mid);
    else queryx(kx<<1|1,mid+1,r);
}

 

這兩個sum[][] 含義不同,是為了適應不同的修改方式

一個是單點修改,一個是矩陣修改

而且矩陣修改不帶標記下傳


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM