1. 程式人生 > 其它 >【Kubernetes系列一】kubernetes1.12安裝

【Kubernetes系列一】kubernetes1.12安裝

可持久化線段樹

可持久化資料結構是可以修改和查詢任意歷史版本的資料結構。

而其中,最簡單的“資料結構”非陣列莫屬了。

設有一題,一個數組,單點修改,單點查詢,不過要求在任意歷史版本為基礎修改,查詢任意歷史版本中的某個元素。

例題點這裡

一看資料範圍,10^6,暴力可以哦豁了。

彳亍吧,那就得另闢蹊徑了

既然只是單點修改,其他的n-1個點沒有被改過呀!於是可持久化資料結構應運而生。如果版本2相比版本1,只是一個元素的差異,那麼只需要儲存版本1,以及多出來的版本2的那一個元素,就OK了

那怎麼聯絡這一個元素,與版本1呢?

如題,用線段樹。

線段樹怎麼用呢?

就像下面這樣用

畫得太好了

橙色代表版本2,黑色代表版本1。

橙樹的左兒子與黑樹左兒子重合(因為版本2的左半區間沒有修改),橙色右兒子的左兒子是它唯一的獨立葉節點(因為這個點代表的元素被修改過),所有橙色的獨立節點代表的區間都被修改過。

所有的節點有且只有2個兒子,所有節點可能不只有1個父節點

所以,一定需要存的是每個版本的修改內容(從根節點下拉一條鏈),空間複雜度\Theta(\log_nq+n)。為了防止空間複雜度退化至\Theta(n\times q),實現時需要動態開點。

結構體?與動態開點一致

struct stree{
   int lc,rc;
   int dat;
   #define lc(x) tr[x].lc
   #define rc(x) tr[x].rc
   #define dat(x) tr[x].dat
}tr[mn*30];//注意算出來是22倍,但由於放大了空間,所以要開30倍

建樹?照著動態開點寫就可以了(到這裡與可持久化沒有關係)

void build(int &now,int l,int r){
   now=++tot;
   if(l==r){
       dat(now)=a[l];
       return;
  }
   int mid=(l+r)>>1;
   build(lc(now),l,mid);
   build(rc(now),mid+1,r);
}

修改?這裡就是重頭戲了。除了標準的動態開點,還需要一個引數來連線該版本中未修改的其他部分原版本

那就再次回到這個圖

顯然,要在某個版本的基礎上進行修改,必先把與之重合的節點複製一遍,至於其他點,就各自新建一個節點就好啦。

void change(int &now,int las,int o,int l,int r,int val){
   now=++tot;
   //新建節點
   dat(now)=dat(las);
   lc(now)=lc(las);rc(now)=rc(las);
   //複製節點
   if(l==r){dat(now)=val;return;}
   int mid=(l+r)>>1;
   if(o<=mid)change(lc(now),lc(las),o,l,mid,val);
   else change(rc(now),rc(las),o,mid+1,r,val);
   //遞迴建整樹
}

查詢?與動態開點模板很像,只需要改改版本方面的引數就可以了。

int ask(int now,int l,int r,int o){
   if(l==r)return dat(now);
   int mid=(l+r)>>1;
   if(o<=mid)return ask(lc(now),l,mid,o);
   else return ask(rc(now),mid+1,r,o);
}

最後,不要忘了新建一個root陣列來存每個版本的根節點。