他支持如下幾種操做:ios
1.建樹(大霧ui
2.單點修改spa
3.單點賦值3d
4.區間修改(加)code
5.區間修改(乘)blog
6.單點查詢遞歸
7.區間求和原型
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int MAXN =100001; struct Segtree { int l,r,mid; int maxn,minn; int w; int tag,tag_mul; }seg[MAXN<<2]; int a[MAXN],ans; void build(int l,int r,int rt) { seg[rt].l = l; seg[rt].r = r; seg[rt].mid = (l+r) >> 1; seg[rt].tag_mul = 1; if(l == r) { seg[rt].w = a[seg[rt].l]; seg[rt].maxn = seg[rt].minn = a[seg[rt].l]; return ; } build(l,seg[rt].mid,rt<<1); build(seg[rt].mid+1,r,rt<<1|1); seg[rt].w = seg[rt<<1].w + seg[rt<<1|1].w; seg[rt].maxn = max(seg[rt<<1].maxn,seg[rt<<1|1].maxn); seg[rt].minn = min(seg[rt<<1].minn,seg[rt<<1|1].minn); return ; } void pushdown_plus(int rt)//標記下傳(加) { seg[rt<<1].tag += seg[rt].tag; seg[rt<<1|1].tag += seg[rt].tag; seg[rt<<1].w += (seg[rt<<1].r - seg[rt<<1].l + 1) * seg[rt].tag; seg[rt<<1|1].w += (seg[rt<<1|1].r - seg[rt<<1|1].l + 1) * seg[rt].tag; seg[rt].tag = 0; return ; } void pushdown_mul(int rt)//標記下傳(乘) { seg[rt<<1].tag += seg[rt].tag; seg[rt<<1|1].tag += seg[rt].tag; seg[rt<<1].tag_mul = seg[rt<<1].tag_mul * seg[rt].tag_mul; seg[rt<<1|1].tag_mul = seg[rt<<1].tag_mul * seg[rt].tag_mul; seg[rt<<1].w = (seg[rt<<1].w * seg[rt].tag_mul + (seg[rt<<1].r - seg[rt<<1].r + 1) * seg[rt].tag); seg[rt<<1|1].w = (seg[rt<<1|1].w * seg[rt].tag_mul + (seg[rt<<1|1].r - seg[rt<<1|1].l + 1) * seg[rt].tag); seg[rt].tag = 0; seg[rt].tag_mul = 1; return ; } void update_plus(int l,int r,int val,int rt)//區間修改(加) { if(seg[rt].l >= l && seg[rt].r <= r) { seg[rt].tag += val; seg[rt].w += (seg[rt].r - seg[rt].l + 1) * val; return ; } if(l <= seg[rt].mid) update_plus(l,seg[rt].mid,val,(rt<<1)); if(r > seg[rt].mid) update_plus(seg[rt].mid+1,r,val,(rt<<1|1)); seg[rt].w = seg[rt<<1].w + seg[rt<<1|1].w; return ; } void update_mul(int l,int r,int val,int rt)//區間修改(乘) { if(seg[rt].r < l || seg[rt].l > r) return ; if(seg[rt].l <= l && seg[rt].r >= r) { seg[rt].w = seg[rt].w * val; seg[rt].tag_mul = seg[rt].tag_mul * val; seg[rt].tag = seg[rt].tag * val; return ; } pushdown_mul(rt); update_mul(l,seg[rt].mid,val,rt<<1); update_mul(seg[rt].mid+1,r,val,rt<<1|1); seg[rt].w = seg[rt<<1].w + seg[rt<<1|1].w; return ; } void query(int l,int r,int rt)//區間求和 { if(seg[rt].l >= l && seg[rt].r <= r) { ans += seg[rt].w; //注意每一次在main內詢問完畢以後要將ans重置 return ; } if(seg[rt].tag) pushdown_plus(rt); if(seg[rt].tag_mul) pushdown_mul(rt); if(l <= seg[rt].mid) query(l,r,(rt<<1)); if(r > seg[rt].mid) query(l,r,(rt<<1|1)); return ; } void modify(int x,int val,int rt)//單點修改x位置爲val { if(seg[rt].l == seg[rt].r == x) { seg[rt].maxn = seg[rt].w = val; return ; } if(x <= seg[rt].mid) modify(x,val,rt<<1); else modify(x,val,rt<<1|1); seg[rt].w = seg[rt<<1].w + seg[rt<<1|1].w; seg[rt].maxn = max(seg[rt<<1].maxn,seg[rt<<1|1].maxn); return ; } int main() { cout << "這是線段樹的naive操做"; return 0; }
2.區間求最大子段和string
這個其實也只是線段樹功能的一部分rwrit
考慮每個區間的最大子段和一共有三種存在方式:所有被包含在左側,所有被包含在右側,被左側和右側各包含一部分
對於前兩種狀況就是向左邊和右邊的子區間遞歸就好了,
對於第三種狀況:咱們對每個線段樹的"區間點"維護一個最大前綴和和最大後綴和,(在這裏最大前綴和就是包含最左邊的點的最大子段和,最大後綴和就是包含最右邊的點的最大子段和)
那麼第三種狀況就是該區間的左側區間最大後綴和加上右側區間的最大前綴和
以該圖爲例,則第1、2、三種狀況分別對應了紅色、黃色、綠色的區間
最大前綴和就是藍色的區間、最大後綴和就是橙色的區間
會轉移了就好寫了把,貼代碼了:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int MAXN = 50001; struct Segtree { int l; int r; int mid; int w; int prel; int prer; int res; }seg[MAXN<<2]; int a[MAXN],n,m; void pushup(int rt) { seg[rt].w = seg[rt<<1].w + seg[rt<<1|1].w; seg[rt].prel = max(seg[rt<<1].prel,seg[rt].w + seg[rt<<1|1].prel); seg[rt].prer = max(seg[rt<<1|1].prer,seg[rt<<1|1].w + seg[rt<<1].prer); seg[rt].res = max(seg[rt<<1].prer + seg[rt<<1|1].prel,max(seg[rt<<1].res,seg[rt<<1|1].res)); } void build(int l,int r,int rt) { seg[rt].l = l; seg[rt].r = r; if(l == r) { seg[rt].res = seg[rt].prel = seg[rt].prer = seg[rt].w = a[seg[rt].l]; return ; } seg[rt].mid = (seg[rt].l + seg[rt].r) >> 1; build(seg[rt].l,seg[rt].mid,rt<<1); build(seg[rt].mid + 1,seg[rt].r,rt<<1|1); pushup(rt); return ; } void modify(int x,int rt,int val) { if(seg[rt].l == seg[rt].r) { seg[rt].prel = seg[rt].prer = seg[rt].res = seg[rt].w = val; return ; } int mid = (seg[rt].l + seg[rt].r) >> 1; if(x <= mid) modify(x,rt<<1,val); else modify(x,rt<<1|1,val); pushup(rt); } /*Segtree query(int l,int r,int rt)//這個是窩寫炸了的東西 { if(l <= seg[rt].l && seg[rt].r <= r) return seg[rt]; int mid = (l + r) >> 1; if(r <= mid) return query(l,r,rt<<1); if(mid < l) return query(l,r,rt<<1|1); Segtree L = query(l,seg[rt].mid,rt<<1); Segtree R = query(seg[rt].mid + 1,r,rt<<1|1); Segtree res; res.w = L.w + R.w; res.prel = max(L.prel,L.w + R.prel); res.prer = max(R.prer,R.w + L.prer); res.res = max(L.prer + R.prel,max(L.res,R.res)); return res; }*/ Segtree query(int x,int y,int rt,int l,int r) { if(x <= l && r <= y) return seg[rt]; int mid = (l + r) >> 1; if(y <= mid) return query(x,y,rt<<1,l,mid); if(mid<x) return query(x,y,rt<<1|1,mid+1,r); Segtree L = query(x,mid,rt<<1,l,mid); Segtree R = query(mid+1,y,rt<<1|1,mid+1,r); Segtree res; res.w = L.w + R.w; res.prel = max(L.prel,L.w + R.prel); res.prer = max(R.prer,R.w + L.prer); res.res = max(L.prer + R.prel,max(L.res,R.res)); return res; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); build(1,n,1); scanf("%d",&m); while(m--) { int opt,x,y; scanf("%d%d%d",&opt,&x,&y); if(opt == 0) { modify(x,1,y); } else { int a6954717 = query(x,y,1,1,n).res; printf("%d\n",a6954717); } } return 0; }
題目要求是詢問區間和,同時在修改的時候是修改等差數列的
好比說修改[1,4] 4 2 ,則其實際是將a[1]+=4,a[2]+=6,a[3]+=8,a[4]+=16;
這裏咱們討論對着一個題的線段樹解法:
普通的線段樹是將其設置爲lazy_tag的下傳
因此咱們仍然考慮如何變形lazy_tag從而使得其仍知足
答案是顯然的,咱們能夠保存該等差數列的a1和d從而使得其能夠經過等差數列求和公式而得出結論
a1能夠直接下傳,d=(seg[rt].r-seg[rt].l+1),這樣就好了
懶得寫代碼了rwr
4.這個和上一個差很少
題目要求是詢問區間和,同時在修改的時候是修改斐波那契數列的
這個題是以長者的p138 T4 爲原型的
同上一個題,能夠發現
a (1,0) (1,0)
b (0,1) (1,1)
a+b (1,1) (2,2)
a+2b (1,2) (3,4)
2a+3b (2,3) (5,7)
3a+5b (3,5) (8,12)
無論是什麼數列,要求知足一個條件就能夠用線段樹了:兩個序列的和仍然爲同一性質的序列
以上