分块

分块

分块相对于线段树而言,是比较通俗易懂的,假设有a1...an个数,利用分块,我们可以做到

1、查询区间和

2、更新区间值

为方便理解,我们设定有16个数,即a1....a16,首先分块,$t=\sqrt{n}$,即将16个数分为四块,每块分别为$\sqrt{n}$的大小

设定一个pos[ ]数组,存入每块的序号,即pos[a1~at]=1,pos[a(t+1)~a(2t)]=2.....如此类推,则可以将区间的每一位数,都分配好相对应的块

设定一个L[ ], R[ ]数组,分别存入每块的边界,如L[1]=a1,R[1]=at;L[2]=a(t+1),R[2]=a(2t)....

设定一个sum[]数组,用于存储每块的区间和。

1、查询区间和——query(l,r)

首先我们假定查询区间a[2~4]之间的和,令l=2,r=4

判断两端点是否处于同一块,p=pos[l],q=pos[r],很明显,这里p=q=1;因此他们属于同一块,直接加起来就好了

 

 但是,如果不处于同一块呢?我们再次假定查询范围是a[2~9];

同样的,我们判断两端点是否处于同一块,此时p=1,q=3,区间和计算如下:

2、更新区间值——update(l,r,val)

我们设定一个add[ ]数组,用来记录每块区间内应加的值,同样的,如果 pos[l],pos[r]属于同一块区间,则直接更新区间内的值

 如果不是一个区间块,则

 分块的时间复杂度为$\sqrt{n}$

 

模板题:分块

AC代码:

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <algorithm> 
 4 #include <cmath>
 5 using namespace std;
 6 typedef long long ll;
 7 const int maxn = 1e5+5;
 8 int L[maxn], R[maxn];  // 每个区间的范围
 9 int pos[maxn];  // 记录所属块
10 ll sum[maxn], a[maxn];
11 ll add[maxn];  // lazy标记
12 
13 void update(int l, int r, int v) {
14     int p = pos[l], q = pos[r];
15     if (p == q) {  // 如果属于同一块
16         for (int i = l; i <= r; i++) a[i] += v;
17         sum[p] += (ll)(r-l+1)*v;
18     }
19     else {
20         for (int i = p+1; i <= q-1; i++) add[i] += v;  // p和q中间的块的add 
21         for (int i = l; i <= R[p]; i++) a[i] += v;
22         sum[p] += (ll)(R[p]-l+1)*v;
23         for (int i = L[q]; i <= r; i++) a[i] += v;
24         sum[q] += (ll)(r-L[q]+1)*v;
25     }
26 }
27 ll query(int l, int r) {
28     int p = pos[l], q = pos[r];
29     ll ans = 0;
30     if (p == q) {
31         for (int i = l; i <= r; i++) ans += a[i];
32         ans += add[p]*(r-l+1);
33     }
34     else {
35         for (int i = p+1; i <= q-1; i++) ans += sum[i] + add[i]*(R[i]-L[i]+1);
36         for (int i = l; i <= R[p]; i++) ans += a[i];
37         ans += add[p]*(R[p]-l+1);
38         for (int i = L[q]; i <= r; i++) ans += a[i];
39         ans += add[q]*(r-L[q]+1);
40     }
41     return ans;
42 }
43 
44 int main() {
45     int n, q; scanf("%d%d",&n,&q);
46     for (int i = 1; i <= n; i++) scanf("%lld",&a[i]);
47     int t = sqrt(n);
48     for (int i = 1; i <= t; i++) {
49         L[i] = (i-1)*t + 1;
50         R[i] = i*t;
51     }
52     if (R[t] < n) t++, L[t] = R[t-1]+1, R[t] = n;
53     for (int i = 1; i <= t; i++) {
54         for (int j = L[i]; j <= R[i]; j++) {
55             pos[j] = i;
56             sum[i] += a[j];
57         }
58     }
59     while (q--) {
60         int op, l, r, v;
61         scanf("%d",&op);
62         if (op == 2) {
63             scanf("%d%d",&l,&r);
64             printf("%lld\n",query(l,r));
65         }
66         else {
67             scanf("%d%d%d",&l,&r,&v);
68             update(l,r,v);
69         }
70     }
71     return 0;
72 }

有什么不懂的欢迎大家向我咨询~虽然我也是一名菜鸟

 

posted @ 2019-10-27 13:47  清蒸母猪  阅读(177)  评论(0编辑  收藏  举报