Skip to content

Latest commit

 

History

History
342 lines (269 loc) · 7.53 KB

File metadata and controls

342 lines (269 loc) · 7.53 KB

前缀和

前缀和可以简单理解为数列的前 $n$ 项的和,是一种重要的预处理方式,能大大降低查询的时间复杂度,如果按照暴力解法,求 $m$ 个数的前 $n$ 项和,时间复杂度为 $O(m*n)$,但是经过前缀和预处理之后,每次查询的时间复杂度为 $O(1)$,求 $m$ 个数时间复杂度就可以降到 $O(n)$

一维前缀和公式和初始化

为了方便,这里前缀和下标统一从 $1$ 开始
一维前缀和初始化

s[0] = 0;

一维前缀和定义

for (int i = 1; i <= n; i ++) s[i] = s[i - 1] + a[i];

二维前缀和公式和初始化

二位前缀和初始化

s[0][0] = 0;

二维前缀和定义

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] - s[i - 1][j - 1] + a[i][j];

二维前缀和计算
计算 $(x1, y1)(x2, y2)$ 子矩阵的和

s[x2][y2] - s[x2][y1 - 1] - s[x1 - 1][y2] + s[x1 - 1][y1 - 1]  

Acwing.795 前缀和

输入一个长度为 $n$ 的整数序列。

接下来再输入 $m$ 个询问,每个询问输入一对 $l,r$

对于每个询问,输出原序列中从第 $l$ 个数到第 $r$ 个数的和。

输入格式
第一行包含两个整数 $n$$m$

第二行包含 $n$ 个整数,表示整数数列。

接下来 $m$ 行,每行包含两个整数 $l$$r$,表示一个询问的区间范围。

输出格式
$m$ 行,每行输出一个询问的结果。

数据范围
$1≤l≤r≤n$,
$1≤n,m≤100000$,
$−1000≤数列中元素的值≤1000$

输入样例:

5 3
2 1 3 6 4
1 2
1 3
2 4

输出样例:

3
6
10
  • 解题代码
#include <iostream>

using namespace std;

const int N = 1e5 + 10;

int n, m;
int s[N], nums[N];

int main(){
    cin >> n >> m;
    
    for (int i = 1; i <= n; i ++) cin >> nums[i];
    
    for (int i = 1; i <= n; i ++) s[i] = s[i - 1] + nums[i];
    
    while (m --){
        int l, r;
        cin >> l >> r;
        
        cout << s[r] - s[l - 1] << endl;
    }
    return 0;
}

Acwing.796 子矩阵的和

输入一个 $n$$m$ 列的整数矩阵,再输入 $q$ 个询问,每个询问包含四个整数 $x1$,$y1$,$x2$,$y2$,表示一个子矩阵的左上角坐标和右下角坐标。

对于每个询问输出子矩阵中所有数的和。

输入格式
第一行包含三个整数 $n$,$m$,$q$。

接下来 $n$ 行,每行包含 $m$ 个整数,表示整数矩阵。

接下来 $q$ 行,每行包含四个整数 $x1$,$y1$,$x2$,$y2$,表示一组询问。

输出格式
$q$ 行,每行输出一个询问的结果。

数据范围
$1≤n,m≤1000$,
$1≤q≤200000$,
$1≤x1≤x2≤n$,
$1≤y1≤y2≤m$,
$−1000≤矩阵内元素的值≤1000$

输入样例:

3 4 3
1 7 2 4
3 6 2 8
2 1 2 3
1 1 2 2
2 1 3 4
1 3 3 4

输出样例:

17
27
21
  • 解题代码
#include <iostream>

using namespace std;

const int N = 1010, M = 2e5 + 10;

int n, m, q;
int s[N][N], nums[N][N];

int main(){
    cin >> n >> m >> q;
    
    for (int i = 1; i <= n; i ++)
        for (int j = 1; j <= m; j ++)
            cin >> nums[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] - s[i - 1][j - 1] + nums[i][j];
            
    while (q --){
        int x1, y1, x2, y2;
        cin >> x1 >> y1 >> x2 >> y2;
        
        cout << s[x2][y2] - s[x2][y1 - 1] - s[x1 - 1][y2] + s[x1 - 1][y1 - 1] << endl;
    }
    return 0;
}

差分

差分是前缀和的逆运算,把 $a$ 称为原数组,如果 $a$$b$ 的前缀和数组,则称 $b$$a$ 的差分数组

b[i] = a[i] - a[i - 1];

差分数组能够在 $O(1)$ 的时间复杂度内计算出将原数组 $[l, r]$ 之间的数加上一个常数后,新数组的值

差分数组的核心操作

void insert(int l, int r, int c){
    b[l] += c;
    b[r + 1] -= c;
}

Acwing.797 差分

输入一个长度为 $n$ 的整数序列。

接下来输入 $m$ 个操作,每个操作包含三个整数 $l,r,c$,表示将序列中 $[l,r]$ 之间的每个数加上 $c$

请你输出进行完所有操作后的序列。

输入格式
第一行包含两个整数 $n$$m$

第二行包含 $n$ 个整数,表示整数序列。

接下来 $m$ 行,每行包含三个整数 $l,r,c$,表示一个操作。

输出格式
共一行,包含 $n$ 个整数,表示最终序列。

数据范围
$1≤n,m≤100000$,
$1≤l≤r≤n$,
$−1000≤c≤1000$,
$−1000≤整数序列中元素的值≤1000$

输入样例:

6 3
1 2 2 1 2 1
1 3 1
3 5 1
1 6 1

输出样例:

3 4 5 3 4 2
  • 解题代码
#include <iostream>

using namespace std;

const int N = 1e5 + 10;

int n, m;
int a[N], b[N];

void insert(int l, int r, int c){
    b[l] += c;
    b[r + 1] -= c;
}

int main(){
    cin >> n >> m;
    
    for (int i = 1; i <= n; i ++) cin >> a[i];
    
    for (int i = 1; i <= n; i ++) insert(i, i, a[i]);// 根据a[i]初始化b[i]
    
    while (m --){
        int l, r, c;
        cin >> l >> r >> c;
        
        insert(l, r, c);// 根据题目要求修改b[i]
    }
    
    for (int i = 1; i <= n; i ++){
        a[i] = a[i - 1] + b[i];// 根据修改后的b[i]更新a[i]
        cout << a[i] << ' ';
    }
    return 0;
}

Acwing.798 差分矩阵

输入一个 $n$$m$ 列的整数矩阵,再输入 $q$ 个操作,每个操作包含五个整数 $x1,y1,x2,y2,c$,其中 $(x1,y1)$$(x2,y2)$表示一个子矩阵的左上角坐标和右下角坐标。

每个操作都要将选中的子矩阵中的每个元素的值加上 $c$

请你将进行完所有操作后的矩阵输出。

输入格式
第一行包含整数 $n,m,q$

接下来 $n$ 行,每行包含 $m$ 个整数,表示整数矩阵。

接下来 $q$ 行,每行包含 $5$ 个整数 $x1,y1,x2,y2,c$,表示一个操作。

输出格式
$n$ 行,每行 $m$ 个整数,表示所有操作进行完毕后的最终矩阵。

数据范围
$1≤n,m≤1000,$
$1≤q≤100000,$
$1≤x1≤x2≤n,$
$1≤y1≤y2≤m,$
$−1000≤c≤1000,$
$−1000≤矩阵内元素的值≤1000$

输入样例:

3 4 3
1 2 2 1
3 2 2 1
1 1 1 1
1 1 2 2 1
1 3 2 3 2
3 1 3 4 1

输出样例:

2 3 4 1
4 3 4 1
2 2 2 2
  • 解题代码
#include <iostream>

using namespace std;

const int N = 1010;

int n, m, q;
int a[N][N], b[N][N];

void insert(int x1, int y1, int x2, int y2, int c){
    b[x1][y1] += c;
    b[x1][y2 + 1] -= c;
    b[x2 + 1][y1] -= c;
    b[x2 + 1][y2 + 1] += c;
}

int main(){
    cin >> n >> m >> q;
    
    for (int i = 1; i <= n; i ++)
        for (int j = 1; j <= m; j ++)
            cin >> a[i][j];
            
    for (int i = 1; i <= n; i ++)
        for (int j = 1; j <= m; j ++)
            insert(i, j, i, j, a[i][j]);// 初始化b[i]差分矩阵
            
    while (q --){
        int x1, y1, x2, y2, c;
        cin >> x1 >> y1 >> x2 >> y2 >> c;
        
        insert(x1, y1, x2, y2, c);// 根据题目要求处理b[i]
    }
    
    for (int i = 1; i <= n; i ++){
        for (int j = 1; j <= m; j ++){
            a[i][j] = a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1] + b[i][j];// 更新a[i]
            cout << a[i][j] << ' ';
        }
        puts("");
    }
    
    return 0;
}