神器! 線性變換線段樹 —— 線段樹入門及應用

在寫這篇博客以前首先申明一下,這個線段樹的名字是個人好隊友 shu_mj 取的。因爲實在很好用,對於新手容易上手因此寫一篇博客造福老百姓。ide

而後說明一下這僅僅是一篇關於線段樹入門的文章。ui

單點更新:spa

單點更新不須要標記整個線段樹只須要自底向上的維護值(最小值,最大值,和等)就好了。實現難度較低,新手也能很快學會。設計

在線段樹中每一個節點的含義都是一個線段根節點表示的是1-n的線段,假設根節點標號是o,他的左子樹標號爲o<<1,右子樹標號爲o<<1|1相應的左子樹表示的區間爲1-m(m=(l+r)>>1),而右子樹表示的區間爲m+1-rcode

這樣咱們用一顆高度爲logn 的樹便可表示整條線段了。blog

對於單點更新的線段樹每一個標記都能下放到最底層,因爲每次下放標記最多沿着一路走因此每次操做的複雜度爲logn的。get

對於求和的時候咱們把區間不斷的拆分用線段樹上的小區間(節點表示的區間)來拼成須要求和的完整區間,最後將這些節點上的標記累加起來即爲答案。複雜度一樣爲logn的。博客

代碼以下:it

 1 int num[LEN], sum[4*LEN], n;
 2 
 3 void pushup(int o){
 4     sum[o] = sum[o<<1] + sum[o<<1|1];
 5 }
 6 
 7 void build(int l, int r, int o){
 8     if(l == r){
 9         //設初值
10         return ;
11     }
12     int m = (l + r)/2;
13     build(l, m, o<<1);
14     build(m+1, r, o<<1|1);
15     pushup(o);
16 }
17 
18 void update(int l, int r, int o, int pos, int val){
19     if(l == r) {
20         sum[o] += val;
21         return ;
22     }
23     int m = (l + r)/2;
24     if(pos <= m) update(l, m, o<<1, pos, val);
25     else update(m+1, r, o<<1|1, pos, val);
26     pushup(o);
27 }
28 
29 int query(int l, int r, int o, int L, int R){
30     if(l >= L && r <= R) return sum[o];
31     int m = (l + r) / 2, ret = 0;
32     if(L <= m) ret += query(l, m, o<<1, L, R);
33     if(R > m) ret += query(m+1, r, o<<1|1, L, R);
34     return ret;
35 }
View Code

區間更新:io

區間更新相比較單點更新來講就要難不少了。首先咱們要設計一個懶惰標記,爲何要設計這個標記呢?

咱們如果在每次更新線段樹的時候都更新至最底層那麼複雜度顯然會退化成線性的,所以咱們給每一個節點上都添加了一個懶惰標記如果不訪問以這個節點爲根的子樹,這個標記就停留在這個節點。等到須要訪問以這個節點爲根的子樹的時候在把標記pushdown到他的左右孩子上。這樣複雜度就維持了nlogn了。在統計的時候和單點更新相似,只是在訪問孩子節點以前須要把懶惰標記下放。

線性變換線段樹:

所謂線性變換,就是咱們的線段樹能處理ax+b這種操做。如今線段樹的經常使用操做有add,mul,set不管是哪一種咱們均可以使用線性變換獲得。

add v: 1*x+v mul v:v*x+0 set v: 0*x+v 

怎麼樣很方便吧。

這樣一來咱們就能夠將這份線段樹存爲模板了,足以應付通常的線段樹題目了。線性變換線段樹因爲維護了多餘的信息多以時間效率可能不那麼使人滿意。

代碼以下:

 1 struct SegmentTree {
 2     ll _sum[4*LEN], _mul[4*LEN], _add[4*LEN];
 3     SegmentTree(){}
 4     
 5     void Display(int l, int r, int o, int d){
 6         if(l == r){
 7             for(int i=0; i<d; i++) cout << "\t";
 8             cout << _sum[o] << "(" << l << ", " << r << ")" << endl;
 9             return ;
10         }
11         int m = (l + r) / 2;
12         Display(l, m, o<<1, d+1);
13         for(int i=0; i<d; i++) cout << "\t";
14         cout << _sum[o] << "(" << l << ", " << r << ")" << endl;
15         Display(m+1, r, o<<1|1, d+1);
16     }
17 
18     void init(){
19         for(int i=0; i<4*LEN; i++){
20             _mul[i] = 1; _add[i] = 0;
21         }
22     }
23 
24     void pushup(int o){
25         _sum[o] = _sum[o<<1] + _sum[o<<1|1];
26     }
27 
28     void push(int o, int l, int r, ll adval, ll muval){
29         _sum[o] *= muval;
30         _mul[o] *= muval;
31         _add[o] *= muval;
32 
33         _sum[o] += (r-l+1) * adval;
34         _add[o] += adval;
35     }
36 
37     void pushdown(int o, int l, int r){
38         int m = (l + r) / 2;
39         push(o << 1, l, m, _add[o], _mul[o]);
40         push(o << 1 | 1, m+1, r, _add[o], _mul[o]);
41         _add[o] = 0; _mul[o] = 1;
42     }
43 
44     void build(int l, int r, int o){
45         if(l == r){
46             scanf("%lld", &_sum[o]);
47             return ;
48         }
49         int m = (l + r) / 2;
50         build(l, m, o<<1);
51         build(m+1, r, o<<1|1);
52         pushup(o);
53     }
54 
55     void update(int l, int r, int o, int L, int R, ll adval, ll muval){
56         if(L <= l && R >= r){
57             push(o, l, r, adval, muval);
58             return ;
59         }
60         pushdown(o, l, r);
61         int m = (l + r) / 2;
62         if(L <= m) update(l, m, o<<1, L, R, adval, muval);
63         if(R > m) update(m+1, r, o<<1|1, L, R, adval, muval);
64         pushup(o);
65     }
66 
67     ll query(int l, int r, int o, int L, int R){
68         if(L <= l && R >= r){
69             return _sum[o];
70         }
71         pushdown(o, l, r);
72         int m = (l + r) / 2;
73         ll ret = 0;
74         if(L <= m) ret += query(l, m, o<<1, L, R);
75         if(R > m) ret += query(m+1, r, o<<1|1, L, R);
76         return ret;
77     }
78 };
View Code

最後,我用了這一份模板ac了一道2013杭州邀請賽的純線段樹的題目在之前不用這份模板的時候我寫了5600K的代碼再ac,而使用了模板以後瞬間代碼量減少2K+

代碼以下:

  1 #include <cstdio>
  2 #include <algorithm>
  3 #define MP(a, b) make_pair(a, b)
  4 using namespace std;
  5 typedef pair<int, int> pii;
  6 typedef pair<int, pii> piii;
  7 const int MOD = 10007;
  8 const int LEN = 100000+10;
  9 
 10 void getsum(piii &a, piii b){
 11     a.first += b.first; a.first %= MOD;
 12     a.second.first += b.second.first; a.second.first %= MOD;
 13     a.second.second += b.second.second; a.second.second %= MOD;
 14 }
 15 
 16 struct SegmentTree {
 17     int _sum[4*LEN], _mul[4*LEN], _add[4*LEN], _sum2[4*LEN], _sum3[4*LEN];
 18 
 19     void init(){
 20         for(int i=0; i<4*LEN; i++){
 21             _mul[i] = 1; _add[i] = 0;
 22             _sum[i] = _sum2[i] = _sum3[i] = 0;
 23         }
 24     }
 25 
 26     void pushup(int o){
 27         _sum[o] = (_sum[o<<1] + _sum[o<<1|1]) % MOD;
 28         _sum2[o] = (_sum2[o<<1] + _sum2[o<<1|1]) % MOD;
 29         _sum3[o] = (_sum3[o<<1] + _sum3[o<<1|1]) % MOD;
 30     }
 31 
 32     void push(int o, int l, int r, int adval, int muval){
 33         _sum3[o] *= (muval * muval % MOD * muval % MOD);
 34         _sum2[o] *= (muval * muval % MOD);
 35         _sum[o] *= muval;
 36         _sum[o] %= MOD; _sum2[o] %= MOD; _sum3[o] %= MOD;
 37         _mul[o] *= muval;
 38         _add[o] *= muval;
 39         _mul[o] %= MOD; _add[o] %= MOD;
 40         
 41         _sum3[o] += (3*adval%MOD*_sum2[o]%MOD + 3*adval%MOD*adval%MOD*_sum[o]%MOD);
 42         _sum3[o] += ((r-l+1)*adval%MOD*adval%MOD*adval%MOD);
 43         _sum2[o] += (2*adval%MOD*_sum[o]%MOD + (r-l+1)*adval%MOD*adval%MOD);
 44         _sum[o] += (r-l+1) * adval;
 45         _sum3[o] %= MOD; _sum2[o] %= MOD; _sum[o] %= MOD;
 46         _add[o] += adval; _add[o] %= MOD;
 47     }
 48 
 49     void pushdown(int o, int l, int r){
 50         int m = (l + r) / 2;
 51         push(o << 1, l, m, _add[o], _mul[o]);
 52         push(o << 1 | 1, m+1, r, _add[o], _mul[o]);
 53         _add[o] = 0; _mul[o] = 1;
 54     }
 55 
 56     void build(int l, int r, int o){
 57         if(l == r){
 58             _sum[o] = _sum2[o] = _sum3[o] = 0;
 59             return ;
 60         }
 61         int m = (l + r) / 2;
 62         build(l, m, o<<1);
 63         build(m+1, r, o<<1|1);
 64         pushup(o);
 65     }
 66 
 67     void update(int l, int r, int o, int L, int R, int adval, int muval){
 68         if(L <= l && R >= r){
 69             push(o, l, r, adval, muval);
 70             return ;
 71         }
 72         pushdown(o, l, r);
 73         int m = (l + r) / 2;
 74         if(L <= m) update(l, m, o<<1, L, R, adval, muval);
 75         if(R > m) update(m+1, r, o<<1|1, L, R, adval, muval);
 76         pushup(o);
 77     }
 78 
 79     piii query(int l, int r, int o, int L, int R){
 80         if(L <= l && R >= r){
 81             return MP(_sum[o], MP(_sum2[o], _sum3[o]));
 82         }
 83         pushdown(o, l, r);    
 84         int m = (l + r) / 2;
 85         piii ret = MP(0, MP(0, 0));
 86         if(L <= m) getsum(ret, query(l, m, o<<1, L, R));
 87         if(R > m) getsum(ret, query(m+1, r, o<<1|1, L, R));
 88         return ret;
 89     }
 90 };
 91 
 92 SegmentTree sg;
 93 
 94 int main()
 95 {
 96     int n, m, op, L, R, val;
 97     while(scanf("%d%d", &n, &m) != EOF){
 98         if(!n && !m) break;
 99         sg.init(); sg.build(1, n, 1);
100         for(int i=0; i<m; i++){
101             scanf("%d%d%d%d", &op, &L, &R, &val);
102             if(op == 1){
103                 sg.update(1, n, 1, L, R, val, 1);
104             }else if(op == 2){
105                 sg.update(1, n, 1, L, R, 0, val);
106             }else if(op == 3){
107                 sg.update(1, n, 1, L, R, val, 0);
108             }else {
109                 piii ans = sg.query(1, n, 1, L, R);
110                 if(val == 1) printf("%d\n", ans.first);
111                 else if(val == 2) printf("%d\n", ans.second.first);
112                 else printf("%d\n", ans.second.second);
113             }
114         }
115     }
116     return 0;
117 }
View Code
相關文章
相關標籤/搜索