日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

可持久化线段树小结

發布時間:2023/12/3 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 可持久化线段树小结 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

學了可持久化線段樹有一段時間了,一直沒拿出時間來整理一下,剛好今天有空,就寫一寫。

可持久化的含義是對于每次修改操作都將產生一個新版本的線段樹,并且舊版本的線段樹仍然保留可以隨時訪問。

基于這個目的,我們可以采用類似動態開點的方法構建一顆線段樹。
假設我們已經有了一個版本的線段樹,我們要對這個線段樹進行操作,我們首先復制前一顆線段樹的根節點,然后在執行修改操作的時候,把所有途經的節點全部新建,其他節點保留。

一、包含單點修改,區間查詢的可持久化線段樹的實現

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);}

總結

以上是生活随笔為你收集整理的可持久化线段树小结的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。