維度探索 二維線段樹

2021-07-30 11:43:43 字數 3482 閱讀 8546

線段樹是乙個神奇的東西,可以o(n)建樹,o(logn)修改、查詢,維護乙個區間的性質。但是線段樹維護的序列一定是一維的,如果我要維護乙個「二維」的結構呢?就比如說,維護乙個矩陣中子矩陣的和。簡單地說就是給你乙個**,每次用「圈出」乙個矩形的部分讓你求它所有元素的和。

沒有學過線段樹的同學們一定要先學一下線段樹一定要先學習一下,再來看這篇部落格。

解決這個問題自然要從靜態(一維)區間和得出靈感。靜態一維區間和我們用的方法是求「字首和」,我們可以用o(n)的時間複雜度求出乙個pre陣列,pre[i]表示閉區間[1,i]對應元素的和。如果我想要求區間[i,j]的區間和,用計算pre[j]-pre[i-1]就可以o(1)解決問題。

同學們可以嘗試著把這個理論推廣到二維,我們可以去維護乙個二維「字首和」來解決這個問題。

區域「i」的矩陣和,就相當於是區域「i+ii+iii+iv」的和減去區域「iii+iv」、減去區域「iii+ii」、再加上區域「iii」。

如果我們用sum(i,j,k,l)表示區域「i」,那麼就有sum(i,j,k,l)=sum(1,j,1,l)-sum(1,i,1,l)-sum(1,j,1,k)+sum(1,i,1,k)。這樣我們就把所有的資料表示成了乙個「二維字首」的形式了。我們可以用pre(i,j)表示sum(1,i,1,j),就有sum(i,j,k,l)=pre(j,l)-pre(i,l)-pre(j,k)+pre(i,k)。

pre(i,j)如何求解呢?顯然可以使用和sum同樣的方法:pre[i][j]=pre[i-1][j]+pre[i][j-1]-pre[i-1][j-1]+a[i][j](a表示原陣列)。

請看偽**:

init:

for i = 1 to n

for j = 1 to m

pre[i][j]=pre[i-1][j]+pre[i][j-1]-pre[i-1][j-1]+a[i][j]

query:

sum(i,j,k,l)=pre[j][l]-pre[i][l]-pre[j][k]+pre[i][k]

二維線段樹,每乙個節點對應乙個子矩陣(根節點代表整體),每個節點有四個兒子節點,分別表示它的「左上,左下,右上,右下」四個部分,例如下圖:

當然,如果邊長不是二的整數次冪也是可以這樣二分的:

這樣我們就得到了乙個建樹的方法:

build(root,left,right,up,down)

if 確定到唯一元素

root.sum=這個元素

else

if 這個區域只有一列

root.左上子=新結點

root.左下子=新結點

int mid=(up+down)/2

build(root.左上子,left,right,up,mid)

build(root.左下子,left,right,mid+1,down)

root.sum=root.左上子.sum+root.左下子.sum

else

if 這個區間只有一行

root.左上子=新結點

root.右上子=新結點

int mid=(left+right)/2

build(root.左上子,left,mid,up,down)

build(root.右上子,mid+1,right,up,down)

root.sum=root.左上子.sum+root.右上子.sum

else

root.左上子=新結點

root.左下子=新結點

root.右上子=新結點

root.右下子=新結點

int midlr=(left+right)/2

int midud=(up+down)/2

build(root.左上子,left,midlr,up,midud)

build(root.左下子,left,midlr,midud+1,down)

build(root.右上子,midlr+1,right,up,midud)

build(root.右下子,midlr+1,right,midud+1,down)

root.sum=root.左上子.sum+root.左下子.sum+root.右上子.sum+root.右下子.sum

查詢還是比較簡單的(因為沒有lazy,不懂的回去複習線段樹!):

query(root,left,right,up,down)

if root == null//這樣當查詢null結點時可以直接忽略掉,不會re

return

0return query(root.左上子,left,right,up,down)+

query(root.左下子,left,right,up,down)+

query(root.右上子,left,right,up,down)+

query(root.右下子,left,right,up,down)

然後是正經的**:

struct node

}ns[1048576];

int newnode=1;//當前亟待申請的節點

#define lst(root) (ns[root].l)

#define rst(root) (ns[root].r)

#define ust(root) (ns[root].u)

#define dst(root) (ns[root].d)//表示乙個結點的區間範圍

#define luch(root) (ns[root].luch)

#define ruch(root) (ns[root].ruch)

#define ldch(root) (ns[root].ldch)

#define rdch(root) (ns[root].rdch)//表示乙個結點的四個兒子

#define sum(root) (ns[root].sum)//這些define可以使**更好理解,但實際上沒什麼必要

int a[101][101];

void build(int root,int l,int r,int u,int d)

else

if(l==r)

else

}int ask(int root,int l,int r,int u,int d)

理論上來講,**與普通線段樹是極其相似的。

趕稿匆忙,如有謬誤,望同學們諒解。

二維線段樹

二維線段樹一般用樹套樹的方式實現,每個外層線段樹的節點對應一顆內層線段樹,整個線段樹存放在乙個二維陣列中。二維線段樹 poj2155 include include include include include include include include include include inc...

二維線段樹

二維線段樹矩陣區間查詢最大值 矩陣求和預處理後o 1 就能算出來,不用線段樹,除非有修改操作 先第一維在第二維,注意建樹有個順序問題,應該讓第一維度的先建完然後再建第二個維度 具體看 include include include using namespace std const int maxn...

二維線段樹

一維線段樹用來維護一維的空間,即乙個線段。二維線段樹用來維護二維的空間,即乙個矩形。二維線段樹的每個結點都是一棵一維線段樹,所以結構體陣列要開二維,再加上線段樹本身的性質,會占用很大記憶體,要儘量減少結構體內儲存的值的個數和長度,考慮到每個節點表示的線段的左右端點可以作為函式引數,所以不再儲存在結構...