可持久化线段树小结
學了可持久化線段樹有一段時間了,一直沒拿出時間來整理一下,剛好今天有空,就寫一寫。
可持久化的含義是對于每次修改操作都將產生一個新版本的線段樹,并且舊版本的線段樹仍然保留可以隨時訪問。
基于這個目的,我們可以采用類似動態開點的方法構建一顆線段樹。
假設我們已經有了一個版本的線段樹,我們要對這個線段樹進行操作,我們首先復制前一顆線段樹的根節點,然后在執行修改操作的時候,把所有途經的節點全部新建,其他節點保留。
一、包含單點修改,區間查詢的可持久化線段樹的實現
struct persist_segtree{struct Node{ //線段樹的節點int lft,rgt; //左兒子指針和右兒子指針int num; //當前節點保存的值}segs[maxn];int rt[maxn]; //歷史版本的線段樹根節點指針int tot; //節點數目void init(){tot = 0;memset(segs,0,sizeof(segs));}inline int getnum(int rt){return rt?segs[rt].num:0;}void add(int &rt,int lft,int rgt,int pos,int num){ //修改操作int nrt = ++tot; //沿途的節點都要新建segs[nrt] = segs[rt]; //新建的節點是原節點的拷貝,然后再修改rt = nrt; int mid = (lft + rgt)/2;if(lft == rgt){ //如果到達底部segs[rt].num += num;return ;}if(pos <= mid) //如果要修改的位置在左側add(segs[rt].lft,lft,mid,pos,num);else //如果要修改的位置在右側add(segs[rt].rgt,mid+1,rgt,pos,num);segs[rt].num = getnum(segs[rt].lft) + getnum(segs[rt].rgt); //更新本節點的值}int query(int rt,int lft,int rgt,int beg,int end){if(!rt) return 0;if(beg <= lft && rgt <= end) return segs[rt].num;if(beg > rgt || end < lft) return 0;int mid = (lft + rgt)/2;return query(segs[rt].lft,lft,mid,beg,end) + query(segs[rt].rgt,mid+1,rgt,beg,end);} }1、查詢,比如要查詢第5個版本線段樹的[3,199919991999]的區間和,那么查詢語句可以這樣寫:
segtree.query(segtree.rt[5],1,1000000000,3,199919991999);2、修改,比如在201720172017位置插入數值12345:
segtree.rt[t] = segtree.rt[t-1]; segtree.add(segtree.rt[t],1,1000000000,201720172017,12345);二、區間修改
我也不知道為什么要把區間修改列在這里,其實區間修改就是lazy標記,和普通的線段樹沒有什么區別,有一點需要注意,就是在 標記下傳 的時候,也要新建節點。 /*標記下傳*//*注意在標記下傳的時候也要新開節點*/void down(int rt,int lft,int rgt){if(!segs[rt].lazy) return ;int mid = (lft + rgt)/2;segs[++tot] = segs[segs[rt].lft];segs[tot].num += segs[rt].lazy*(mid-lft+1);segs[tot].lazy += segs[rt].lazy;segs[rt].lft = tot;segs[++tot] = segs[segs[rt].rgt];segs[tot].num += segs[rt].lazy*(rgt-mid);segs[tot].lazy += segs[rt].lazy;segs[rt].rgt = tot;segs[rt].lazy = 0;}查詢和修改操作也要根據lazy標記做適當的修改
void add(int &rt,int lft,int rgt,int beg,int end,int num){int nrt = ++tot;segs[nrt] = segs[rt];rt = nrt;int mid = (lft + rgt)/2;if(beg <= lft && rgt <= end){segs[rt].num += (rgt-lft+1)*num;segs[rt].lazy += num;return ;}if(rgt < beg || end < lft) return ;down(rt,lft,rgt);if(lft <= end) add(segs[rt].lft,lft,mid,beg,end,num);if(rgt >= beg) add(segs[rt].rgt,mid+1,rgt,beg,end,num);segs[rt].num = getnum(segs[rt].lft) + getnum(segs[rt].rgt);}int query(int rt,int lft,int rgt,int beg,int end){if(!rt) return 0;if(beg <= lft && rgt <= end) return segs[rt].num;if(beg > rgt || end < lft) return 0;int mid = (lft + rgt)/2;down(rt,lft,rgt);return query(segs[rt].lft,lft,mid,beg,end) + query(segs[rt].rgt,mid+1,rgt,beg,end);}總結
- 上一篇: 电脑如何设置修改BIOS电脑如何更改开机
- 下一篇: 二元运算 FFT+分治