概述
就是可持久化的線段樹。更直白一點,就是 很多個線段樹套在一起(共用信息相同的節點)。
幹嘛
可以解決區間第大等神奇問題。
優點
快。這不是廢話嗎
普通版の實現
0x01初始化
一開始,我們需要一顆線段樹。就按照普通的建樹方法即可。
0x02建新樹
- 要素是 不改變原有的節點。因爲本質是很多不同的線段樹,只是用一些奇技淫巧來省空間(和時間)而已。
- 首先,將原來的樹的 對應節點複製到當前節點。包括子節點是誰。也就是說,現在這兩個點 共用了相同的子節點。
- 然後考慮子節點——如果有修改,則 申請新節點,遞歸的建樹。否則不動。
在理解這個玩意兒的時候,你就這麼想:其實兩個點的子節點不同,沒有共用子節點,只是子節點住一間屋子而已。反正我們也不會把房子拆了,即修改原有的節點。
也就是說,本質是這樣的兩顆線段樹。綠色的編號表示 內存地址。共用內存罷了。
——前提條件是這兩個節點 信息相同!
這樣做的好處,在 單點修改 時很明顯:只需要新建個點。因爲只有這一條單鏈上的點(即紅色的點)與原有的節點信息不同。
或者放點代碼?
void modify(int old,int &o,int id){
o = cntNode ++; // 申請新節點
node[o] = node[old]; // 複製舊節點
if(node[old].l == node[old].r){ // 葉子節點,直接更改信息
++ node[o].ppl;
return ;
}
if(id <= (node[o].l+node[o].r)>>1) // 說明右子樹與原有的節點信息是相同的
modify(node[old].lson,node[o].lson,id);
else // 遞歸修改子樹
modify(node[old].rson,node[o].rson,id);
pushUp(o); // 更新當前節點信息
}
- 但是,就像普通的線段樹,任何查詢操作都需要 知道根是誰。所以你得用一個數組把第個線段樹的根存下來。
順便一說,區間修改 就必須使用奇技淫巧了。
- 標記永久化。就是 捨棄,遞歸時自己惦記着。
- 下傳也開新點。爲了不修改原有節點嘛。
0x03查詢
當成普通線段樹。——別忘了 人類的本質 主席樹的本質就是 真香! 普通線段樹的省空間版本。
動態開點の線段樹
0x01思想
權值線段樹在權值很大的時候得離散化。很蛋疼的 於是我們繼續用 共用內存 的思想——
任意兩個從未修改過的點,信息(左右端點除外)一定相同!(至少在大多數權值線段樹中如此)。
所以我們可以把它們全部搞到一個專門存“虛點”的內存裏。比如號點。
0x02初始化
我們一開始 不需要建好樹——因爲整棵樹都是“虛點”。直接申請一個虛點作爲根,還順便搞到了“虛點”的地址呢!
0x03建新樹
同正常版。雖說叫做“虛點”,實際上還是一種共用內存的思想。但是這樣的共用內存 不純粹。因爲左右端點並不相同。好消息:我們用不到左右端點!因爲“虛點”是無需訪問子節點的;要訪問,就代表有修改,就會變成“實點”。
懶得話在函數傳參時傳進去也沒啥
例題
0x01板題
如題。
// 離散化,正常版
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int MaxN = 200005;
struct segmentTreeNode{ // 權值線段樹
int lson, rson, l, r;
int ppl; // PPL orz or2 orz
}node[MaxN<<5];
int root[MaxN], cntNode;
void pushUp(int pos){
node[pos].ppl = node[node[pos].lson].ppl+node[node[pos].rson].ppl;
}
void build(int &o,int l,int r){
o = cntNode ++;
node[o].l = l; node[o].r = r;
if(l != r){
build(node[o].lson,l,(l+r)>>1);
build(node[o].rson,(l+r)/2+1,r);
pushUp(o);
}
else{ // is a leaf
node[o].ppl = 0;
}
}
void modify(int old,int &o,int id){
o = cntNode ++;
node[o] = node[old];
if(node[old].l == node[old].r){
++ node[o].ppl;
return ;
}
if(id <= (node[o].l+node[o].r)>>1)
modify(node[old].lson,node[o].lson,id);
else
modify(node[old].rson,node[o].rson,id);
pushUp(o);
}
int query(int old,int o,int k){
if(node[o].l == node[o].r)
return node[o].l;
if(node[node[o].lson].ppl-node[node[old].lson].ppl >= k)
return query(node[old].lson,node[o].lson,k);
k -= (node[node[o].lson].ppl-node[node[old].lson].ppl);
return query(node[old].rson,node[o].rson,k);
}
int lsh[MaxN], a[MaxN], n;
int zxyMeier[MaxN];
void getLSH(){
for(int i=0; i<n; ++i)
zxyMeier[i] = a[i];
sort(zxyMeier,zxyMeier+n);
for(int i=0; i<n; ++i)
lsh[i] = lower_bound(zxyMeier,zxyMeier+n,a[i])-zxyMeier;
}
int main(){
int q;
scanf("%d %d",&n,&q);
for(int i=0; i<n; ++i)
scanf("%d",&a[i]);
getLSH();
build(root[0],1,n);
for(int i=1; i<=n; ++i)
modify(root[i-1],root[i],lsh[i-1]+1);
for(int i=1,l,r,k; i<=q; ++i){
scanf("%d %d %d",&l,&r,&k);
printf("%d\n",zxyMeier[query(root[l-1],root[r],k)-1]);
}
return 0;
}
// ---------------密封線---------------
// 請 勿 在 密 封 線 內 答 題
// 動態開點版,非常牛(sao)逼(qi)
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int MaxN = 200005, infty = 1e9;
struct segmentTreeNode{
int lson, rson;
int ppl; // PPL orz or2 orz
segmentTreeNode(){
ppl = lson = rson = 0;
}
}node[MaxN<<5];
int root[MaxN], cntNode;
int newNode(){ // 動態開點!
return cntNode ++;
} // 儘管這個函數看着挺傻的……
# define mid ((l+r-1)>>1) // 用來處理負數域
void pushUp(int pos){
node[pos].ppl = node[node[pos].lson].ppl+node[node[pos].rson].ppl;
}
void modify(int old,int &o,int l,int r,int id){
o = newNode();
node[o] = node[old];
if(l == r){ // is a leaf
++ node[o].ppl;
return ;
}
if(id <= mid)
modify(node[old].lson,node[o].lson,l,mid,id);
else
modify(node[old].rson,node[o].rson,mid+1,r,id);
pushUp(o);
}
int query(int old,int o,int l,int r,int k){
if(l == r) return l; // a leaf
if(node[node[o].lson].ppl-node[node[old].lson].ppl >= k)
return query(node[old].lson,node[o].lson,l,mid,k);
k -= (node[node[o].lson].ppl-node[node[old].lson].ppl);
return query(node[old].rson,node[o].rson,mid+1,r,k);
}
int a[MaxN], n;
int main(){
int q;
scanf("%d %d",&n,&q);
for(int i=1; i<=n; ++i)
scanf("%d",&a[i]);
root[0] = newNode(); // 創建“虛點”
for(int i=1; i<=n; ++i)
modify(root[i-1],root[i],-infty,infty,a[i]);
for(int l,r,k; q; --q){
scanf("%d %d %d",&l,&r,&k);
printf("%d\n",query(root[l-1],root[r],-infty,infty,k));
}
return 0;
}
0x02板題二號
題目描述
(閱讀程序,猜測題目)
提示:很板!(所以不想打題目描述)
#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
const int MaxN = 100005;
struct segmentTreeNode{
int lson, rson, l, r;
int maxV;
}node[MaxN<<3];
int root[MaxN], cntNode;
int val[MaxN];
void pushUp(int pos){
node[pos].maxV = max(node[node[pos].lson].maxV,node[node[pos].rson].maxV);
}
void build(int &o,int l,int r){
o = cntNode ++;
node[o].l = l; node[o].r = r;
if(l != r){
build(node[o].lson,l,(l+r)>>1);
build(node[o].rson,(l+r)/2+1,r);
pushUp(o);
}
else
node[o].maxV = val[l];
}
void modify(int old,int &o,int id,int v){
o = cntNode ++;
node[o] = node[old];
if(node[old].l == node[old].r){
node[o].maxV = v;
return ;
}
if(id <= (node[old].l+node[old].r)>>1)
modify(node[old].lson,node[o].lson,id,v);
else
modify(node[old].rson,node[o].rson,id,v);
pushUp(o);
}
int query(int o,int l,int r){
if(l <= node[o].l and node[o].r <= r)
return node[o].maxV;
if(r <= (node[o].l+node[o].r)>>1)
return query(node[o].lson,l,r);
if(l > (node[o].l+node[o].r)>>1)
return query(node[o].rson,l,r);
return max(query(node[o].lson,l,r),query(node[o].rson,l,r));
}
int main(){
int n, q;
scanf("%d %d",&n,&q);
for(int i=1; i<=n; ++i)
scanf("%d",&val[i]);
build(root[1],1,n);
int edition = 1;
while(q --){
int cmd, k, one, two;
scanf("%d %d %d %d",&cmd,&k,&one,&two);
if(cmd == 0)
printf("%d\n",query(root[k],one,two));
else
modify(root[k],root[++ edition],one,two);
}
return 0;
}