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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

线段树-简单线段树模板

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

簡單線段樹

2021年7月30

線段樹是干什么的?

更新,維護,查詢某區間的某項值,如區間和等。

線段樹的原理

與樹狀數組類似,線段樹通過直接建樹來保存區間的結點,如圖:

如何建圖?

下面以求序列區間和為例:

當存放一個數字時,它需要將每個子區間的值返回到父節點之上,依次返回直到最大的區間上。此時,一個區間接收到的值就是它所有子區間值的和。

由于每個區間的大小都為1<<i,0<=i<=n,因此,我們構建的樹是一顆二叉樹。按行進行標號,易得第i個區間的兩個子區間標號分別為i * 2與i * 2 + 1。

依據這個性質,易想到使用遞歸方式進行存圖。代碼如下:

int built(int i, int l, int r){tre[i].l=l;tre[i].r=r;//更新第i個區間的左右邊界if(l==r) return tre[i].sum=a[i];//若區間大小為1,則更新值后直接返回int mid=(l+r/2);return tre[i].all+=built(i*2,l,mid)+built(i*2+1,mid+1,r);//其區間和等于左右子樹的區間和,更新后返回 }
單點更新

對于每次查找,只需要找到其位置,更新即可

void onemodify(int i,int pos,int k){if (tre[i].r<l || tre[i].l>r)return;if (tre[i].l >= l && tre[i].r <= r) {tre[i].all += k;}modify(i * 2, l, r, k);modify(i * 2 + 1, l, r, k); }
如何對整個區間進行修改?

給出區間l與r,要求區間內每個點加k。從樹上進行遍歷,對每個在區間l-r內的點進行更新值。

void modify(int i, int l, int r, int k) {if (tre[i].r<l || tre[i].l>r)return;//不在區間內,直接返回if (tre[i].l <= l && tre[i].r >= r) {tre[i].all += k*(min(r-l,tre[i].r - tre[i].l) +1);//更改區間在目前結點內,更新最大可更新值}modify(i * 2, l, r, k);modify(i * 2 + 1, l, r, k); }

但是,這種更新方式,其復雜度是O(nlog(n)),再加上m次更新,其復雜度達到了O(nmlog(n)),比直接枚舉還要多了log級別的時間復雜度。

如何將其優化到O(log(n))的復雜度?

增加一個標記:懶惰標記

此標記意味著對于此區間內所有子區間的結點,都有增加了k。

void modify(int i, int l, int r, int k){if (tre[i].r<l || tre[i].l>r)return;tre[i].all += k * (min(r, tre[i].r) - max(l, tre[i].l) + 1);//兩區間交集就是需要更新值的部分if ((tre[i].l >= l && tre[i].r <= r)) {tre[i].lazy += k;return;}modify(i * 2, l, r, k);modify(i * 2 + 1, l, r, k); }

此時,就不用查找到每一個長度為1的區間了。

注:此時需在建圖時將tre中的lazy初始化為0.

模板樣例

第一行輸入三個數n,m,q,分別為數組大小,修改次數及查詢次數,隨后一行有n個數字,分別是a1…n,第3行到第2+m行,每行三個數,分別為區間邊界及每個數修改的值,接下來q行,每行兩個數,分別為要查詢區間的左右邊界。

每次查詢,輸出一行數字,代表區間和。

輸入樣例:

8 4 7 1 5 2 3 6 9 7 2 2 3 2 3 5 7 3 3 1 4 6 8 1 8 3 3 3 3 2 5 4 8 6 8 5 7

輸出樣例:

85 12 12 58 65 26 45
代碼
#include<iostream> #include<algorithm> #include<stdio.h> #include<string> #include<string.h>using namespace std;struct Tree {int l, r;int all;int lazy; }tre[1005];int n, m, q; int sic[1005], ans; string row;int builtre(int i, int l, int r) {tre[i].l = l; tre[i].r = r; tre[i].lazy = 0;if (l == r)return tre[i].all=sic[l];int mid = (l + r) / 2;tre[i].all += builtre(i * 2, l, mid);tre[i].all += builtre(i * 2 + 1, mid + 1, r);return tre[i].all; }void modify(int i, int l, int r, int k) {if (tre[i].r<l || tre[i].l>r)return;tre[i].all += k * (min(r, tre[i].r) - max(l, tre[i].l) + 1);if ((tre[i].l >= l && tre[i].r <= r)) {tre[i].lazy += k;return;}modify(i * 2, l, r, k);modify(i * 2 + 1, l, r, k); }int solve(int i, int l, int r,int lazy) {if (tre[i].l >= l && tre[i].r <= r) {ans += tre[i].all;ans += (tre[i].r - tre[i].l + 1) * lazy;return ans;}if (tre[i].l > r || tre[i].r < l)return 0;if (tre[i].r >= l) solve(i * 2, l, r,lazy + tre[i].lazy);if (tre[i].l <= r) solve(i * 2 + 1, l, r, lazy + tre[i].lazy);return ans; }int main() {cin >> n >> m >> q;for (int i = 0; i < n; i++){cin >> sic[i + 1];}builtre(1, 1, n);int a, b, k;while (m--) {cin >> a >> b >> k;modify(1, a, b, k);}while (q--) {cin >> a >> b;ans = 0;cout << solve(1, a, b, tre[1].lazy) << endl;}return 0; }

后練習代碼

#include<iostream> #include<algorithm> #include<stdio.h> #include<string> #include<string.h> typedef long long ll;using namespace std;int n, m; int sic[100005];struct P {int l, r;int key, lazy; }tre[100005];int built(int p, int l, int r) {tre[p].l = l; tre[p].r = r;if (l == r)return tre[p].key = sic[l];int mid = (l + r >> 1);return tre[p].key = built(p * 2, l, mid) + built(p * 2 + 1, mid + 1, r); }void insert(int p, int l, int r, int key) {if (tre[p].r<l || tre[p].l>r)return;if (tre[p].l >= l && tre[p].r <= r) {tre[p].lazy += (tre[p].r-tre[p].l+1)*key; return;}tre[p].lazy += (1+min(r - l, min(tre[p].r - l, r - tre[p].l))) * key;insert(2 * p, l, r, key); insert(2 * p + 1, l, r, key); }int findkey(int p, int l, int r) {if (tre[p].r<l || tre[p].l>r)return 0;if (tre[p].l >= l && tre[p].r <= r) return tre[p].key + tre[p].lazy;return findkey(2 * p, l, r) + findkey(2 * p + 1, l, r); }int main() {cin >> n;for (int i = 1; i <= n; i++){cin >> sic[i];}built(1, 1, n);while (1) {int a, l, r, k;cin >> a;if (!a)break;cin >> l >> r;if (a == 2) {cin >> k;insert(1, l, r, k);}elsecout << findkey(1, l, r) << endl;}return 0; }

總結

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

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