聽名字非常的厲害了,它是線段樹的高階應用.
考慮如果你需要很多很多線段樹的話,你需要開很多很多空間.但是如果這些線段樹之間非常相似,我們可以把這乙個線段樹和上乙個線段樹共用大部分的節點.
比如乙個對與乙個陣列建立主席樹,乙個個的把元素放進線段樹的話當然可以把重新做乙個線段樹,但每次只有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除錯然後考慮把各個數插入線段樹並形成新的版本.和單點修改一樣,它也只改變logn次即管自己的那幾個節點,我們可以把新的節點記錄一下,從新的根節點出發,一定先新建乙個節點,把自己的管的範圍內的數的數量sum++,如果插入的數歸左兒子管就去更新左兒子,否則去更新右兒子.如何更新呢?這是乙個遞迴的過程:把這個兒子複製成乙個新節點,更新sum與左右兒子.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個點管著,時間複雜度是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最...
線段樹 動態開點
在一些計數問題中,線段樹用於維護值域 一段權值範圍 這樣的線段樹也稱為權值線段樹。為了降低空間複雜度,我們可以不建出整棵線段樹的結構,而是在最初只建立乙個根節點,代表整個區間,當需要訪問線段樹的某棵子樹 某個子區間 時,再建立代表這個子區間的節點。採用這種方法維護的線段樹稱為動態開點的線段樹。動態開...