一、定義
二維線段樹,即用線段樹維護一個矩陣
有兩種實現方式:
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[][] 含義不同,是為了適應不同的修改方式
一個是單點修改,一個是矩陣修改
而且矩陣修改不帶標記下傳