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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

模板:线段树

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

文章目錄

  • 引言
  • 思想
  • 模板
    • 建樹
    • 單點修改 / 查詢
    • 區間修改/查詢
  • 總結
  • 練習


引言

有一類題目:要求在區間上維護信息,比如帶修改區間求和問題。考慮到枚舉求和肯定會超時,我們可以通過一些數據結構來維護信息,例如線段樹。
它功能強大,支持區間求和,區間求最值、區間修改等一系列操作。

思想

線段樹,是一種二叉搜索樹(即每個結點最多有兩棵子樹,通常叫做左右子樹)。
它將一段區間劃分為若干單位區間,每一個節點都儲存著一段區間[L,R]的信息,其中葉子節點L=R。它的大致思想是:將區間[1,n]平均分成2個小區間,再將小區間平均分成2個更小區間…以此類推,直到區間長度變為1無法分成兩個更小的區間。通過對這些小區間進行修改、查詢,來實現對大區間的修改、查詢。
用線段樹維護的問題必須滿足可以用兩個小區間的信息合并成大區間信息,否則是不可能將大問題劃分成子問題來解決的。

下圖就是一棵[1,10]的線段樹的分解過程。(相同顏色的節點在同一層)
可以證明,線段樹的最大深度不超過log2n

模板

整體來說都是在貫徹遞歸二分分治的思想。注意k結點的兩個孩子編號應分別為2k 和 2k+1
注意:關于線段樹的數組要開到4n(最差時)!

建樹

思想:常見的做法是遍歷整棵線段樹,設當前遍歷到的節點所代表的區間為[l,r],設中點mid=(l+r)/2,則左子樹區間[l,mid],右子樹的區間為[mid+1,r],注意要遞歸到線段樹的葉節點才結束,并給每一個節點賦值,葉子節點直接賦值,非葉子節點信息由左右子樹信息合并而來。

void build(int k,int l,int r){if(l==r){sum[k]=f[l];return;}int mid=(l+r)>>1;build(2*k,l,mid);build(2*k+1,mid+1,r);sum[k]=sum[2*k]+sum[2*k+1]; }

單點修改 / 查詢

用樹狀數組不好嗎
思想:如果當前遍歷到目標節點,就返回節點信息。如果不是,設查詢位置為x,當前結點區間范圍為了[l,r],中點為mid,若x≤mid,則x在左子樹的區間,遞歸當前結點的左孩子;否則x在右子樹的區間,遞歸它的右孩子。

int dotquery(int k,int l,int r,int x){if(l==x)return sum[k];int mid=(l+r) /2;int res=0;if(x<=mid) return dotquery(2*k,l,mid,x);else return dotquery(2*k+1,mid+1,r,x); } void dotchange(int k,int l,int r,int x,int v){if(l==r){sum[k]+=v;return;}int mid=(l+r) /2;if(x<=mid) dotchange(2*k,l,mid,x,v);else dotchange(2*k+1,mid+1,r,x,v);sum[k]=sum[2*k]+sum[2*k+1]; }

區間修改/查詢

區間查詢:
設待查詢區間為[x,y],如果當前遍歷到的區間被查詢區間包含,就直接返回節點信息。如果不是,設當前結點區間范圍為[l,r]中點為mid,如果x≤mid,則待查詢區間與[l,mid]有交集,遞歸[l,mid];如果y>mid,則待查詢區間與[mid+1,r]有交集,遞歸[mid+1,r],最后將返回的信息合并就是區間[x,y]的答案。
區間修改:
延續區間查詢的思想,我們可以找出所有信息發生改變的節點,可現在有一核心問題:我們并不能直接在這些節點上修改,若全部修改,復雜度難以承受。所以,區間修改的關鍵思路是:只修改對查詢有用的點。為此,我們引入“懶標記"的概念。
懶標記:若遍歷到的節點帶有懶標記,則立即修改當前節點的信息,并把標記下放到左右兒子,然而左右子樹的信息不變動,只有遍歷到左/右節點時才修改其信息

void Add(int k,int l,int r,int v){add[k]+=v;sum[k]+=(ll)(r-l+1)*v;return; } void pushdown(int k,int l,int r,int mid){if(add[k]==0) return;Add(2*k,l,mid,add[k]);Add(2*k+1,mid+1,r,add[k]);add[k]=0; } void longchange(int k,int l,int r,int x,int y,int v){if(x<=l&&r<=y){Add(k,l,r,v);return;}int mid=(l+r)>>1;pushdown(k,l,r,mid);if(x<=mid) longchange(2*k,l,mid,x,y,v);if(y>=mid+1) longchange(2*k+1,mid+1,r,x,y,v);sum[k]=sum[2*k]+sum[2*k+1]; } ll longquery(int k,int l,int r,int x,int y){if(x<=l&&r<=y) return sum[k];int mid=(l+r)>>1;pushdown(k,l,r,mid);ll res=0;if(x<=mid) res+=longquery(2*k,l,mid,x,y);if(y>=mid+1) res+=longquery(2*k+1,mid+1,r,x,y);return res; }

總結

線段樹是很常用強大的數據結構,其功能也遠不止求最值和加和這么簡單,要想真正掌握,還是要背模板 理解其深層的運作機制

練習

洛谷題單傳送門

歡迎各位dl點贊評論,謝謝 收聽 拜讀觀看! awa

總結

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

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