分块入门(线段树区间修改)

例题地址:嘟嘟嘟html

 

分块其实我早都据说过,并且怎么回事差很少都清楚了,只是一直没有写。如今离NOIP2018也挺近了,考虑到分块有时候确实能水到很多的分,决定这几天写一写。ios

 

众所周知,分块就是对于一个询问区间[L, R],属于恰好一个块中的部分就整块处理,多出来的散的部分就暴力处理。令块大小为S,则共有⌊n / S⌋块,每一次操做多出来的部分不会超过2 * S个,那么最坏复杂度是O(⌊n / S⌋ + 2 * S) 。为了均衡,就取了S = √n。git

对于这道题,仍是要以一个表示数组add,但这个标记没那么复杂,不用下传,只是为了询问单个元素的时候用。具体操做是这样的:数组

1.修改:对于区间[L, R],令l = L / S + 1, r = R / S + 1。那么 l + 1块到 r - 1块必定是整块,直接修改sum数组。对于前面的零散部分,即[L, l * S - 1](后面有证实为啥是这个)和后面的[(r - 1) * S, R],就直接修改a[i]。ide

2.询问:和修改极其像。对于整块的,ret += sum[i] (i 此时表示第几个块);对于单个的,ret += a[i] + add[k](k = i / S + 1)。post

上面的证实:只要求出来第k个块所在区间便可:由于⌊i / S⌋ + 1 = k,因此⌊i / S⌋ = k - 1,则 i ∈ [(k - 1) * S, (k - 1) * S + S - 1] => i ∈ [(k - 1) * S, k * S - 1]。那么[L, R]中前半部分就是[L, l * S - 1],后半部分就是[(r - 1) * S, R]。spa

上代码code

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cmath>
 4 #include<algorithm>
 5 #include<cstring>
 6 #include<cstdlib>
 7 #include<cctype>
 8 #include<vector>
 9 #include<stack>
10 #include<queue>
11 using namespace std;
12 #define enter puts("") 
13 #define space putchar(' ')
14 #define Mem(a, x) memset(a, x, sizeof(a))
15 #define rg register
16 typedef long long ll;
17 typedef double db;
18 const int INF = 0x3f3f3f3f;
19 const db eps = 1e-8;
20 const int maxn = 1e5 + 5;
21 inline ll read()
22 {
23     ll ans = 0;
24     char ch = getchar(), last = ' ';
25     while(!isdigit(ch)) {last = ch; ch = getchar();}
26     while(isdigit(ch)) {ans = ans * 10 + ch - '0'; ch = getchar();}
27     if(last == '-') ans = -ans;
28     return ans;
29 }
30 inline void write(ll x)
31 {
32     if(x < 0) x = -x, putchar('-');
33     if(x >= 10) write(x / 10);
34     putchar(x % 10 + '0');
35 }
36 
37 int n, m, S;
38 
39 int bel(int x)
40 {
41     return x / S + 1;
42 }
43 ll a[maxn], sum[maxn], add[maxn];
44 void update(int L, int R, int k)
45 {
46     int l = bel(L), r = bel(R);
47     if(l == r)        //别忘不够一个块的状况 
48     {
49         for(int i = L; i <= R; ++i) a[i] += k, sum[bel(L)] += k;
50         return;
51     }
52     for(int i = l + 1; i < r; ++i) sum[i] += k * S, add[i] += k;
53     for(int i = L; i <= l * S - 1; ++i) a[i] += k, sum[bel(i)] += k;
54     for(int i = (r - 1) * S; i <= R; ++i) a[i] += k, sum[bel(i)] += k;
55 }
56 ll query(int L, int R)
57 {
58     ll ret = 0;
59     int l = bel(L), r = bel(R);
60     if(l == r)
61     {
62         for(int i = L; i <= R; ++i) ret += a[i] + add[l];
63         return ret;
64     }
65     for(int i = l + 1; i < r; ++i) ret += sum[i];
66     for(int i = L; i <= l * S - 1; ++i) ret += a[i] + add[bel(i)];
67     for(int i = (r - 1) * S; i <= R; ++i) ret += a[i] + add[bel(i)];
68     return ret;
69 }
70 
71 int main()
72 {
73     n = read(); m = read(); S = sqrt(n);
74     for(int i = 1; i <= n; ++i) a[i] = read(), sum[bel(i)] += a[i];
75     for(int i = 1; i <= m; ++i)
76     {
77         int d = read(), L = read(), R = read();
78         if(d == 1) 
79         {
80             ll k = read();
81             update(L, R, k);
82         }
83         else write(query(L, R)), enter;
84     }
85     return 0;
86 }
View Code