前缀和

学习前缀和

什么是前缀和

对于一组数 a1, a2, a3, a4, a5

其前缀和为 Si = a1 + a2 + .. + ai

如何求前缀和

在其中 i 的初始下标为 1, 我们可以得到递推公式 $S_i = S_{(i-1)} + a[i]$

让 i 从 1 开始且定义 S[0] = 0, 这是为了方便我们计算出指定区间的元素和

前缀和有什么用

比如当我们计算区间[l,r]的所有数的和, 就可以直接 S[r] = S[l-1]

现在要求 [1,10]之前的和, 就可以直接 S[10] - S[0]即可得解

s[r]-s[l-1]

为啥不直接 for 循环求[l, r]

因为如果我们有很多个 [l,r]区间要求, 那么就得来十次 for 循环, 而采用前缀和只需要一次从头到尾的 for 循环后面直接相减即可

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const int N = 100010;

int a[N], S[N];

int main(){
    int m,n;
    scanf("%d%d", &n, &m);
    
    
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
        S[i] = S[i-1] + a[i];
        
    }
    
    for(int i = 0; i < m; i++){
        int l,  r;
        scanf("%d%d", &l, &r);
        printf("%d\n", S[r]-S[l-1]);
    }
    
    
    return 0;
}

例题: 前缀和

二维前缀和

同理, 二维数组的前缀和也是每个 index 对应的 value 之和

IMG_6D7F95814E49-1

二维前缀和的作用?

求子矩阵的和

现在我们要求 S33 和 S11 围起来的这块子矩阵的和, 那么可以根据前缀和求解.

他等于 S33 - S31 - S13 + a00

可以看到这个一维前缀和s[r]-s[l-1] 是类似的, 只不过最后要加上一块缺的角

即: 求 [x1][y1]到 [x2][y2]的公式为 S[x2][y2] - S[x1-1][y2] - S[x2][y2-1] + a[x1-1][y1-1]

构造二维前缀和

首先肯定要有原始数组矩阵, 这个原始数组最好有效元素都从 index=1 开始

有了原始数组, 就可以从 1 开始构造了前缀和矩阵了

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
const int N = 1010, M = 1010;
int a[N][M];
int S[N][M];
int main(){
    int n,m,q;
    scanf("%d%d%d", &n,&m,&q);
    
    //构造原始数组矩阵
    for(int i = 1; i<=n; i++)
        for(int j = 1; j <= m; j++) {
            scanf("%d",&a[i][j]);
        }
    
    //计算前缀和
    for(int i = 1; i<=n; i++)
        for(int j = 1; j <= m; j++) {
            S[i][j] = S[i-1][j] + S[i][j-1] + a[i][j] - S[i-1][j-1];
        }
    
    //求子矩阵的和
    for(int i=0; i < q; i++){
        int  x1,y1,x2,y2;
        scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
        int tmp = S[x2][y2] - S[x1 -1][y2] - S[x2][y1-1] + S[x1-1][y1-1];
        printf("%d\n", tmp);
        
    }
    
    
    return 0;
}

例题: 子矩阵的和