emmmmm….html
昨天剛學徹底線段樹,今天先來記錄一波….c++
指不定以後還須要寫更多的東西….?git
因此說目前爲止只學習了基本操做和懶標記我也很無奈.jpg數組
線段樹的基本操做數據結構
學習線段樹的平常基礎思考:學習
給定一個長度爲n的數組,對其中某段序列進行m次以下可能操做:字體
固然,這些操做均可以只用一個數組a進行模擬來完成優化
如今咱們分析一下複雜度:對於操做一、5來講,每次的時間複雜度爲O(1),由於只須要修改或輸出數組a[i]的值就行了,可是對於操做二、三、4來講,每次的時間複雜度爲O(區間長度)(時間複雜度計算我根本不會因此是搬來的現成數據)ui
在理論上的最壞狀況下,所須要的時間複雜度爲O(mn)spa
對於這樣的複雜度,小於十萬的數據或許還行,大於十萬就很是棘手了
所以咱們須要可以進行較大規模數據的數據結構。
例如:線段樹。
線段樹本質上是維護下標爲1,2,..,n的n個按順序排列的數的信息,因此,實際上是「點樹」,是維護n的點的信息,至於每一個點的數據的含義能夠有不少,
在對線段操做的線段樹中,每一個點表明一條線段,在用線段樹維護數列信息的時候,每一個點表明一個數,但本質上都是每一個點表明一個數。如下,在討論線段樹的時候,區間[L,R]指的是下標從L到R的這(R-L+1)個數,而不是指一條連續的線段。只是有時候這些數表明實際上一條線段的統計結果而已。
線段樹是將每一個區間[L,R]分解成[L,M]和[M+1,R] (其中M=(L+R)/2 這裏的除法是整數除法,即對結果下取整)直到 L==R 爲止。
下面咱們用一張圖來演示線段樹對於區間的劃分過程
以長度爲13的序列舉例
劃分過程以下
如上圖,每個節點都表明一段區間的信息
這就是區間樹。能夠證實,二叉區間樹的深度爲logn(本文中全部的log均爲log2)
由於在這個樹上,咱們最終將這個區間都劃分紅了長度爲1的區間
所以實際上咱們要對點進行操做時或對區間進行操做時,只須要不斷劃分區間便可。
由於當咱們在區間樹上找到一個區間屬於當前要操做的區間時,其從屬的區間沒有必要進行查找,那麼顯然在區間樹上查詢一個區間的值的時間複雜度是log級別的
這樣的話咱們就能將單點操做和區間操做的時間複雜度進行均攤,達到log級別。
由於線段樹實際是以點來表示區間的,即:每個點上所記錄的實際是對應區間的信息
一下對與須要對二叉樹的點進行說明時,用「區間」代替點
那麼咱們來口頭實現一下最初要求實現的操做:
對單個節點進行修改,咱們不斷遞歸進行二分,知道枚舉到對應的葉子節點,而後修改便可
對於區間修改來講,咱們須要經過遞歸找到全部對應的區間(包括對應區間的子區間),而後進行修改
對於區間查詢來講,只須要找到對應的區間便可
下面放一下代碼?
我仍是放具體題目而後丟大段代碼吧…優秀板子我將放出某gy神的博客的連接…高端代碼自取系列:http://www.cnblogs.com/hinanawitenshi/p/8093624.html
懶標記
咱們先對代碼進行一下分析:
對於操做:
單點修改只是修改樹上的一條路徑,長度最長爲logn,所以複雜度爲O(logn)
對於區間和最大(小)值查詢,實際上仍是對一條路徑進行搜索,並且要用到的長度更小,就算極限,複雜度也只是O(logn)
可是對於區間修改,咱們一次修改的是一個區間的全部點,最壞狀況下,咱們要對1—n全部的數都進行修改,實際是修改了整棵樹,好像複雜度並無獲得改善,那麼咱們就得另想他法了:
仔細想一想好像沒有什麼辦法,由於要修改的話彷佛必須遍歷全部的數,可是實際上,咱們爲何必定要進行修改呢,仔細想一想,對於一個區間,咱們除了在對他的左右兒子區間進行查詢而進行劃分時,會須要他的左右兒子區間,可是其他時刻咱們好像並不須要用到,那麼咱們不妨只對這個區間對應的值進行修改,而後咱們打上一個標記,表示若是須要劃分這個區間,那麼這個區間的左右兒子的值也須要修改。這個標記,咱們叫作懶標記
那麼這樣的話咱們是否達到了優化區間修改的做用呢?
對於區間修改來講,咱們僅將原先要修改的子樹的根節點打上了懶標記,而後返回,所以其時間複雜度與區間的詢問是相同的,即O(logn)
對於區間詢問來講,咱們只是在遍歷到帶有標記的節點時,纔對標記進行相關的處理,而關於標記的處理的複雜度顯然是O(1)的,對總體的時間複雜度不形成影響,即區間詢問的時間複雜度仍然是O(logn)
建樹只是增長了標記的初始化,單點操做則不須要進行初始化,其時間複雜度不變
這樣咱們就將線段樹一次修改的總體複雜度降到了O(logn)
#include <bits/stdc++.h> #define maxn 100057 using namespace std; struct tre{ int delta,maxx,sum; }tree[maxn]; int n,m; int a[maxn]; void pushdown(int pos,int l,int r){//懶標記下傳 if(!tree[pos].delta)return; int lc=pos*2,rc=pos*2+1,m=(l+r)/1,vv=tree[pos].delta; tree[lc].sum+=(m-l+1)*vv,tree[rc].sum+=(r-m+1)*vv; tree[lc].maxx+=vv,tree[rc].maxx+=vv; tree[lc].delta+=vv,tree[rc].delta+=vv; tree[pos].delta=0; } void maintain(int pos){//從新計算區間的最大值和區間和 int lc=pos*2,rc=pos*2+1; tree[pos].maxx=max(tree[lc].maxx,tree[rc].maxx); tree[pos].sum=tree[lc].sum+tree[rc].sum; } void build(int pos,int l,int r){//建樹 if(l==r){ tree[pos].delta=0; tree[pos].maxx=tree[pos].sum=a[l]; return; } int m=(l+r)/2; build(pos*2,l,m); build(pos*2+1,m+1,r); maintain(pos); } int query_max(int pos,int L,int R,int l,int r){//區間最大值查詢 if(l>R||r<L) return 0; if(l>=L&&r<=R) return tree[pos].maxx; pushdown(pos,l,r); int m=l+r>>1; return max(query_max(pos<<1,L,R,l,m),query_max((pos<<1)+1,L,R,m+1,r)); } int query_sum(int pos,int L,int R,int l,int r){//區間和查詢 if(l>R||r<L) return 0; if(l>=L&&r<=R) return tree[pos].sum; pushdown(pos,l,r); int m=l+r>>1; return query_sum(pos*2,L,R,l,m)+query_sum(pos*2+1,L, R,m+1,r); } void updata(int pos,int L,int R,int l,int r,int v){//區間修改 if(l>R||r<L) return; if(l>=L&&r<=R){//此大括號內容根據題意自定 tree[pos].delta+=v; tree[pos].maxx+=v; tree[pos].sum+=(r-l+1)*v; return; } pushdown(pos,l,r); int m=(l+r)/2; updata(pos*2,L,R,l,m,v); updata(pos*2+1,L,R,m+1,r,v); maintain(pos); } int main(){ cin>>n>>m; for(int i=1;i<=n;i++) cin>>a[i]; build(1,1,n); while(m--){ int bj,x,y,k; cin>>bj; if(bj==1){ cin>>x>>y>>k; updata(1,x,y,1,n,k); } else if(bj==2){ cin>>x>>y; cout<<query_sum(1,x,y,1,n)<<endl; } else{ cin>>x>>y cout<<query_max(1,x,y,1,n)<<endl; } } return 0; }
不要在乎這醜陋的碼風和莫名其妙的斜字體
關於區間奇數位和偶數位和的求解
對於一個區間的奇數位和偶數位,咱們能夠知道的是,區間總和減去奇數位和等於偶數位和
可是在維護時,區間有一些須要注意的問題
好比一個大區間
[1, 2, 3, 4, 5, 6]
二分後獲得[1, 2, 3]和[4, 5, 6]
顯然其中奇數位1,3,5.然而按照最初建樹時狀況來看,咱們記錄的是1, 3和4,6的值
那麼如何獲得5的值的,能夠用右邊區間的總和 減去所記錄的奇數位和
由這個例子推廣便可。
對於查詢,咱們要如何知道咱們在查的是奇數位和仍是偶數位和呢?
對於一個大區間[L, R]中,咱們查到了一個小區間[l, r]
若l - L是奇數,咱們能夠知道的是,從L到l中,有算上l的三個數,因此l就是整個區間的偶數位
而l對於小區間[l, r]來講,是奇數位,因此對於小區間[l, r]咱們就要取偶數位,不然就取奇數位
舉例代碼,20181025九校聯考T2
1 #include<bits/stdc++.h> 2 #define ll long long 3 using namespace std; 4 const int maxn = 500010; 5 const int maxm = 1000010; 6 const int mod = 1000000007; 7 struct shiki { 8 ll sum, delta, l; 9 }tree[maxm << 3]; 10 ll c[maxn << 1]; 11 int n, m; 12 ll fac[maxn], inv[maxn]; 13 ll ans_ma = 0, ans_mi = 0; 14 15 inline ll read() { 16 ll x = 0, y = 1; 17 char ch = getchar(); 18 while(!isdigit(ch)) { 19 if(ch == '-') y = -1; 20 ch = getchar(); 21 } 22 while(isdigit(ch)) { 23 x = (x << 1) + (x << 3) + ch - '0'; 24 ch = getchar(); 25 } 26 return x * y; 27 } 28 29 inline ll power(ll a, ll b) { 30 ll res = 1; 31 for(; b; b >>= 1) { 32 if(b & 1) res = res * a % mod; 33 a = a * a % mod; 34 } 35 return res; 36 } 37 38 inline void init() { 39 fac[1] = 1, inv[1] = 1; 40 for(int i = 2; i <= maxn; ++i) { 41 fac[i] = (fac[i - 1] * i) % mod;//階乘 42 inv[i] = power(fac[i], mod - 2) % mod;//逆元 43 } 44 } 45 46 inline ll C(int n, int m) {//n!/m!(n-m)! = n! * m!^mod-2 * (n - m)!^mod-2 47 return fac[n] * inv[m] % mod * inv[n - m] % mod;} 48 49 inline void maintain(int pos, int l, int r) { 50 int lc = pos << 1, rc = pos << 1 | 1, mid = l + r >> 1; 51 tree[pos].sum = tree[lc].sum + tree[rc].sum; 52 tree[pos].l = tree[lc].l + (((mid - l + 1) & 1) ? tree[rc].sum - tree[rc].l : tree[rc].l); 53 tree[pos].sum %= mod, tree[pos].l %= mod; 54 } 55 56 inline void pushdown(int pos, int l, int r) { 57 if(!tree[pos].delta) return; 58 int lc = pos << 1, rc = pos << 1 | 1, mid = l + r >> 1, del = tree[pos].delta; 59 tree[lc].sum += (mid - l + 1) * del, tree[lc].l += (mid - l + 2) / 2 * del; 60 tree[rc].sum += (r - mid) * del, tree[rc].l += (r - mid + 1) / 2 * del; 61 tree[lc].sum %= mod, tree[lc].l %= mod; 62 tree[rc].sum %= mod, tree[rc].l %= mod; 63 tree[lc].delta += del, tree[rc].delta += del; 64 tree[pos].delta = 0; 65 } 66 67 void build(int pos, int l, int r){ 68 if(l == r) { 69 tree[pos].sum = c[l]; 70 tree[pos].l = c[l]; 71 return; 72 } 73 int mid = l + r >> 1; 74 build(pos << 1, l, mid); 75 build(pos << 1 | 1, mid + 1, r); 76 maintain(pos, l, r); 77 } 78 79 void update(int pos, int L, int R, int l, int r, int val) { 80 if(l > R || r < L) return ; 81 if(l >= L && r <= R) { 82 tree[pos].sum += (r - l + 1) * val; 83 tree[pos].delta += val; 84 tree[pos].l += (r - l + 2) / 2 * val; 85 tree[pos].l %= mod, tree[pos].sum %= mod; 86 return; 87 } 88 if(l != r) pushdown(pos, l, r); 89 int mid = l + r >> 1; 90 update(pos << 1, L, R, l, mid, val); 91 update(pos << 1 | 1, L, R, mid + 1, r, val); 92 maintain(pos, l, r); 93 } 94 95 ll query_sum(int pos, int L, int R, int l, int r) { 96 if(l > R || r < L) return 0; 97 if(l >= L && r <= R) return tree[pos].sum % mod; 98 if(l != r) pushdown(pos, l, r); 99 int mid = l + r >> 1; 100 return (query_sum(pos << 1, L, R, l, mid) + query_sum(pos << 1 | 1, L, R, mid + 1, r)) % mod; 101 } 102 103 ll query_l(int pos, int L, int R, int l, int r) { 104 if(l > R || r < L) return 0; 105 if(l >= L && r <= R) 106 return ((l - L) & 1) ? tree[pos].sum - tree[pos].l : tree[pos].l; 107 if(l != r) pushdown(pos, l, r); 108 int mid = l + r >> 1; 109 return (query_l(pos << 1, L, R, l, mid) + query_l(pos << 1 | 1, L, R, mid + 1, r)) % mod; 110 } 111 112 int main() { 113 freopen("sort.in", "r", stdin); 114 freopen("sort.out", "w", stdout); 115 init(); 116 n = read(), m = read(); 117 for(int i = 1; i <= 2 * n; ++i) c[i] = read(); 118 // sort(c + 1, c + 2 * n + 1); 119 if(n <= 5100) { 120 for(int i = 1; i <= m; ++i) { 121 ll opt = read(), l = read(), r = read(); 122 if(l > r) swap(l, r); 123 if(opt == 0) { 124 ll val = read(); 125 for(int j = l; j <= r; ++j) 126 c[j] += val; 127 } 128 else if(opt == 1) { 129 ll ln = (r - l + 1), lm = (r - l + 1) >> 1, mid = (l + r) >> 1; 130 ll op = C(ln, lm) % mod * power(lm + 1, mod - 2) % mod; 131 ans_ma = 0, ans_mi = 0; 132 for(int j = l; j <= mid; ++j) ans_ma -= c[j]; 133 for(int j = mid + 1; j <= r; ++j) ans_ma += c[j]; 134 for(int j = l; j <= r; j += 2) ans_mi -= c[j]; 135 for(int j = l + 1; j <= r; j += 2) ans_mi += c[j]; 136 printf("%lld %lld %lld\n", ans_ma, ans_mi, op); 137 } 138 } 139 return 0; 140 } 141 else { 142 n = 2 * n; 143 build(1, 1, n); 144 for(int i = 1; i <= m; ++i) { 145 ll opt = read(), l = read(), r = read(); 146 if(l > r) swap(l, r); 147 if(opt == 0) { 148 ll val = read(); 149 update(1, l, r, 1, n, val); 150 } 151 else if(opt == 1) { 152 ll ln = (r - l + 1), lm = (r - l + 1) >> 1, mid = (l + r) >> 1; 153 ll op = C(ln, lm) % mod * power(lm + 1, mod - 2) % mod; 154 ans_ma = (query_sum(1, mid + 1, r, 1, n) - query_sum(1, l, mid, 1, n) + mod) % mod; 155 ans_mi = (query_l(1, l + 1, r, 1, n) - query_l(1, l, r, 1, n) + mod) % mod; 156 printf("%lld %lld %lld\n", ans_ma, ans_mi, op); 157 } 158 } 159 } 160 return 0; 161 }