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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

数据结构(终极线段树篇)

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

數據結構(終極線段樹篇)

摘要:

問題的提出:如何解決多樣化的區間操作問題?

solve:線段樹!!!

關鍵字:

線段樹,可持久化線段樹,權值線段樹,線段樹森林,動態開點線段樹,區間操作,線段樹應用。

前言:

區間操作問題的解決方法極多,如:樹狀數組,RMQ等。

所有這些數據結構都具有一定的局限性,都具有各自的優勢和劣勢。

而線段樹無疑是這些數據結構中性價比最高的數據結構!(Ps:搞什么線段樹,暴力出奇跡啊!)

線段樹,線段樹,線段樹。

顧名思義:樹上每一個節點表示一個線段(區間),每一次對一整個線段(區間)進行操作,棒棒。

? ? ? ? 第1節講述了線段樹的基本性質。

? ? ? ? 第2節實現了基本線段樹。

? ? ? ? 第3節講述動態開點線段樹。

? ? ? ? 第4節講述權值線段樹。

? ? ? ? //第5節講述線段樹森林。

? ? ? ? //第6節講述線段樹的重要應用和例題。

?

1.線段樹的基本性質:

?? ? ? ?1.0基本線段樹的性質:

? ? ? ? ? ?1.每個節點u表示一個區間[l,r]。若節點u非葉子節點,則u有兩個兒子。設(mid=(l+r)/2)。 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 左兒子為[l,mid],節點編號為u*2。 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 右兒子為[mid+1,r],節點編號為u*2+1。

? ? ? ? ? ? 2.線段樹的深度為O(lgn)

? ? ? ? ? ? 3.線段樹的節點個數為O(n),常數約為4。

? ? ? ? ? ? 4.線段樹上任意一個區間[l,r]都可以被O(logn)個線段覆蓋。

? ? ? ? ? ??

? ? ? ? ? ? ?思考:為何左兒子不為[l,mid+1],為何左兒子的節點編號為u*2。

? ? ? ? ? ? ?思考:如何證明線段樹的節點個數為O(n),且常數為4。

? ? ? ? ? ? ?思考:如何證明線段樹上任意一個區間[l,r]都可以被O(logn)個線段覆蓋。

?

?

倘若我們只需要單點修改,我們只需要找到相應的葉子節點,更改葉子節點并沿路更新。

但倘若我們需要區間修改,我們卻需要引入lazy tag(懶惰標記)。

每一次操作都先下放懶標記,清空標記,再進行相應的操作。

?

如上圖:若將[1,5]全加上3。

需要標記tag[1,5]=3,下一次操作到[1,5]時下放。

tag[1,3]=3,tag[4,5]=3,tag[1,5]=0,sum[1,5]+=3*5;

?

2.基本線段樹的實現:

? ? ? ? 2.1基本線段樹的區間加,詢問區間和:

#include<bits/stdc++.h> #define ll long long using namespace std; const int MAXN=1000005; ll a[MAXN]; ll read() {char c=getchar(); ll f=1,x=0;while (!isdigit(c)) {if (c=='-') f=-1; c=getchar();}while (isdigit(c)) {x=x*10+c-'0'; c=getchar();}return f*x; } struct Segment_tree {struct node{int l,r;ll sum,tag;} tree[MAXN<<2];void up(int x){ tree[x].sum=tree[x<<1].sum+tree[(x<<1)|1].sum; }void down(int x){int num1=(tree[x<<1].r-tree[x<<1].l+1);int num2=(tree[(x<<1)|1].r-tree[(x<<1)|1].l+1);tree[(x<<1)|1].sum+=tree[x].tag*num2; tree[x<<1].tag+=tree[x].tag; tree[(x<<1)|1].tag+=tree[x].tag; tree[x<<1].sum+=tree[x].tag*num1; tree[x].tag=0;}void build(int x,int l,int r,ll *a){tree[x].l=l;tree[x].r=r;if (l==r){tree[x].sum=a[l];tree[x].tag=0;return;}int mid=(l+r)>>1;build(x<<1,l,mid,a);build((x<<1)|1,mid+1,r,a);up(x);}void change(int x,int l,int r,ll y){if (tree[x].l>=l&&tree[x].r<=r){tree[x].tag+=y;tree[x].sum+=y*(tree[x].r-tree[x].l+1);return;}down(x);int mid=(tree[x].l+tree[x].r)>>1;if (r<=mid) change(x<<1,l,r,y);else if (l>mid) change((x<<1)|1,l,r,y);else {change(x<<1,l,mid,y);change((x<<1)|1,mid+1,r,y);}up(x);}ll query(int x,int l,int r){if (tree[x].l>=l&&tree[x].r<=r) return tree[x].sum;down(x);int mid=(tree[x].l+tree[x].r)>>1;if (r<=mid) return query(x<<1,l,r);else if (l>mid) return query((x<<1)|1,l,r);else return query(x<<1,l,mid)+query((x<<1)|1,mid+1,r);}void print(int x){for (int i=1;i<=x;i++) printf("%d %d %d\n",tree[i].l,tree[i].r,tree[i].sum);printf("\n");} } segment_tree; int main() {int n=read(),m=read();for (int i=1;i<=n;i++) a[i]=read();segment_tree.build(1,1,n,a);for (int i=1;i<=m;i++){int c=read();if (c==1){int x=read(),y=read(),z=read();segment_tree.change(1,x,y,z);}else{int x=read(),y=read();printf("%lld\n",segment_tree.query(1,x,y));}}return 0; }

?

3.動態開點線段樹

?

動態開點線段樹,實現單點加,區間求和。

#include<bits/stdc++.h> #define ll long long using namespace std; const int MAXN=1000005; const int MAXS=1e9; ll read() {char c=getchar(); ll f=1,x=0;while (!isdigit(c)) {if (c=='-') f=-1; c=getchar();}while (isdigit(c)) {x=x*10+c-'0'; c=getchar();}return f*x; } struct Dynamic_segment_tree {int nodenum=0;struct node{int l,r,leftnode,rightnode;ll sum;} tree[MAXN<<2];void change(int &x,int l,int r,int t,ll y){if (!x){nodenum++;x=nodenum;}tree[x].l=l;tree[x].r=r;tree[x].sum+=y;if (l==r) return;int mid=(tree[x].l+tree[x].r)>>1;if (t<=mid) change(tree[x].leftnode,l,mid,t,y);else change(tree[x].rightnode,mid+1,r,t,y);}ll query(int x,int l,int r){if (!x) return 0;if (tree[x].l>=l&&tree[x].r<=r) return tree[x].sum;int mid=(tree[x].l+tree[x].r)>>1;if (r<=mid) return query(tree[x].leftnode,l,r);else if (l>mid) return query(tree[x].rightnode,l,r);else return query(tree[x].leftnode,l,mid)+query(tree[x].rightnode,mid+1,r);}void print(int x){for (int i=1;i<=x;i++) printf("%d %d %d %d %d\n",tree[i].l,tree[i].r,tree[i].leftnode,tree[i].rightnode,tree[i].sum);printf("\n");} } dynamic_segment_tree; int main() {int n=read(),m=read(),root=0;for (int i=1;i<=m;i++){int c=read(),x=read(),y=read();if (c==1) dynamic_segment_tree.change(root,1,MAXS,x,y);else printf("%d\n",dynamic_segment_tree.query(root,x,y));//dynamic_segment_tree.print(20);}return 0; }

4.權值線段樹

權值線段樹,就是每一個線段樹的葉子節點記錄一種值的信息,常用于記錄區間內某權值的出現個數。

這和一般的線段樹差不多,只是節點的意義不同。

例題:求逆序對個數

?

未完待續

總結

以上是生活随笔為你收集整理的数据结构(终极线段树篇)的全部內容,希望文章能夠幫你解決所遇到的問題。

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