離散化,動態開點,歷史版本,主席樹p2086

2022-03-29 18:43:13 字數 2846 閱讀 7304

聽名字非常的厲害了,它是線段樹的高階應用.

考慮如果你需要很多很多線段樹的話,你需要開很多很多空間.但是如果這些線段樹之間非常相似,我們可以把這乙個線段樹和上乙個線段樹共用大部分的節點.

比如乙個對與乙個陣列建立主席樹,乙個個的把元素放進線段樹的話當然可以把重新做乙個線段樹,但每次只有logn個節點遭到更新或者新建,可以考慮如何壓縮空間.

嗯我只理解到了這裡,以後繼續更新

先用三行離散化一下,處理出總的大小不同的數的數量sum

o是原陣列,a是用來離散化的.sort一下後a固然會變成有序的,但是unique這句話出來把a內從a[1]到a[n]值不同的數量賦值給sum外還會把這些數值不同的數挑乙個放到前面,後面放的不知道是啥.這樣子每次查詢乙個數是第幾大的話就可以直接寫 k=lower_bound(a+1,a+1+sum,要查的數)-a-1;

然後先用tot++新建節點的方式新建乙個樹,沒有要維護的資訊,只是先把左右節點弄一下.設rt為最大的根節點的位置,lc為左端點,rc為右端點,ll表示管得到的最小的數(對應離散化之後的數),rr表示管得到的最小的數.這裡舉個栗子:

對於n=15的這個陣列,顯然只有十個本質不同的樹.拿他來建樹的話就是這樣的乙個結構:

(哎呀這個紫色也太好看了)

而普通(正常)的線段樹

如果把樹畫出來其實可以發現是乙個dfs序,這是和普通的線段樹不一樣的地方.雖然看起來少了一些點,但是它要多開兩個陣列記錄兒子節點的位置,但普通線段樹左右兒子一定是now*2與now*2+1所以不用記錄,也不知道誰比較好.但是這個時候比沒啥必要,人家主席樹還要logn的空間記錄以後的資訊了.

紀念一下除錯**:

int

i;int a[10000

],n,m;

int tot,rt[100],lc[100],rc[100],ll[100],rr[100

];//

rt=root lc左兒子 rc右耳子

void build(int &t,int l,int

r)

int mid=(l+r)/2

; build(lc[t],l,mid);

build(rc[t],mid+1

,r);

return;}

void put(int

hh)int

main()

除錯然後考慮把各個數插入線段樹並形成新的版本.和單點修改一樣,它也只改變logn次即管自己的那幾個節點,我們可以把新的節點記錄一下,從新的根節點出發,一定先新建乙個節點,把自己的管的範圍內的數的數量sum++,如果插入的數歸左兒子管就去更新左兒子,否則去更新右兒子.如何更新呢?這是乙個遞迴的過程:把這個兒子複製成乙個新節點,更新sum與左右兒子.

每個點被logn個點管著,時間複雜度是n*logn,每次新增logn個點,空間多了一倍而已.

然後就是查詢如何做了.查詢區間第k大的本質仍是查詢區間內區間數的數量,又是乙個遞迴的過程.

考慮區間[1,y]的[l,r]查詢就與正常的線段樹查詢一樣,只不過進去的時候由根節點=1變為rt[y].查詢[x,y]的話就需要用一下小小容斥[1,y][l,r]-[1,x-1][l,r].然後怎麼查第k大呢?考慮同時檢視兩個地方.整個的主席樹等價於n個線段樹,每個線段樹維護的是只有[1,i]這個範圍的數時區間[l,r]的sum.要查詢根節點為x,y的第k大(這是兩個線段樹),對於當前的區間[l,r]可以求出sum[l,mid],如果它大於k說明第k小應該在區間[l,r]裡,我們遞迴進入lc[x],lc[y]管這的子樹中,他們管的區間是[l,mid],繼續查詢第k大;否則應該去rc[x],rc[y]子樹中,查詢[mid+1,r]中的k-sum[l,mid]大值.最後當l==r時返回下標,它是在離散化陣列中的位置.

最後給出總的**.

int

i,tx,ty,k;

int a[200010],o[200010

];int rt[200010],lc[200010

<<5],rc[200010

<<5],c[200010

<<5

],tot;

intn,m,sum;

void build(int &now,int l,int

r)int built(int now,int l,int

r)int ask(int x,int y,int l,int r,int

k)int

main()

for(;m;m--)

return0;

}

主席樹

問題 D 陽光雨露 離散化 線段樹 動態開點

思路 1e9嘛.賽中沒有動態開點的板子,手寫調1小時多最後mle,麻了。第二天改了動態的寫法,然後re。瘋狂re。剛剛改出來re70分。先放離散化的寫法吧。真的方便很多.賽中為什麼想不開阿 include include include include include include include...

線段樹動態開點

為了降低權值線段樹的空間複雜度,可以不直接建出整棵線段樹的結構,而是在最初只建立乙個根節點,當需要訪問某棵為建立的子樹的時候,再建立代表這個子樹的節點。動態開點的線段樹用變數記錄左右節點的編號。值域為1 n的動態開點線段樹在m次單點修改後,節點規模為o mlogn 例題 p1908 逆序對 這題n最...

線段樹 動態開點

在一些計數問題中,線段樹用於維護值域 一段權值範圍 這樣的線段樹也稱為權值線段樹。為了降低空間複雜度,我們可以不建出整棵線段樹的結構,而是在最初只建立乙個根節點,代表整個區間,當需要訪問線段樹的某棵子樹 某個子區間 時,再建立代表這個子區間的節點。採用這種方法維護的線段樹稱為動態開點的線段樹。動態開...