前缀和和差分模板

说明

不论是一维还是二维
前缀和的作用都是快速求解某个区间的数据和
差分的作用都是快速进行区间数值的修改

[注]:上方将某个区间标记为了重点,在做题时一旦遇到区间内数量等字眼,应当及时反应到前缀和

一维前缀和

#include <iostream>

using namespace std;

const int N = 1e5 + 10;

int n, m;
int a[N];

int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; ++ i)
    {
        cin >> a[i];
        a[i] += a[i - 1]; // 原数组直接作为前缀和数组
    }
    while (m --)
    {
        int l, r;
        cin >> l >> r;
        cout << a[r] - a[l - 1] << endl;
    }
    return 0;
}

一维差分

#include <iostream>

using namespace std;

const int N = 1e5 + 10;

int n, m;
int b[N];

void insert(int l, int r, int v)
{
    b[l] += v;
    b[r + 1] -= v;
}
int main()
{
    cin >> n >> m;
    // 构造差分数组
    for (int i = 1; i <= n; ++ i)
    {
        int v;
        cin >> v;
        insert(i, i, v); // 把原数组全部看为0, 读入操作可以等效为修改操作。区别于传统的差分数组构造方法,和后续操作相统一
    }
    // 进行修改
    while (m --)
    {
        int l, r, v;
        cin >> l >> r >> v;
        insert(l, r, v);
    }
    // 求差分的前缀和即得原始数组
    for (int i = 1; i <= n; ++ i)
    {
        b[i] += b[i - 1];
        cout << b[i] << ' ';
    }
    cout << endl;
    return 0;
}

二维前缀和

#include <iostream>

using namespace std;

const int N = 1010;

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

int main()
{
    cin >> n >> m >> q;
    for (int i = 1; i <= n; ++ i)
    {
        for (int j = 1; j <= m; ++ j)
        {
            cin >> s[i][j];
            s[i][j] += s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1]; // 二维前缀和数组构造
        }
    }
    while (q --)
    {
        int x1, y1, x2, y2;
        cin >> x1 >> y1 >> x2 >> y2;
        cout << s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1] << endl;
    }
    return 0;
}

二维差分

一维差分用于快速的区间修改,二维差分用于快速的区域修改。
理解二维差分的前提是认识到当b[i, j]发生改变后,右下角所有前缀和包含b[i, j]的元素的值都会发生对应的改变
以下图为例,其中标星的区域是待修改元素。目标是修改红色区域内数值,由于红色区域的左侧和上侧数值均不会受到影响,因此只需要保证橙色、绿色和紫色区域的数值不变,橙色和绿色区域进行一次红色的反操作即可抵消红色区域进行的修改,紫色区域受到红色、橙色、绿色的同时影响,红色和橙色或绿色其中之一发生抵消,因此紫色区域需要进行红色的相同操作,即可与剩余的橙色或绿色发生抵消

/**
 * 二维差分的真实含义已经不再和一维一样清晰了,为了更好理解,把它看为一个便于修改矩阵数值的工具更好一些
 * 最难理解的位置在于insert函数进行的那4个操作,想要理解这个,需要联系差分的前缀和的求法,前缀和就是求左上角
 * 所有方格的数值和,也就是说当b[i, j]发生改变后,右下角所有前缀和包含b[i, j]的元素的值都会发生对应的改变
 * 比如b[i, j]+2,效果就是让原数组中在[i, j]右下角的所有元素值都+2,前缀和对应的就是原数组的值,而差分数组的改变对应的就是前缀和的改变也就对应着原始数组的改变
 * 理解了b[i, j]的改变会影响它右下角所有元素的值,结合insert的目的就不难理解那4句话了
 */
#include <iostream>

using namespace std;

const int N = 1010;

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

void insert(int x1, int y1, int x2, int y2, int v) // 作用:将左上角为[x1, y1],右下角为[x2, y2]的矩阵中的数值都+v
{
    b[x1][y1] += v;
    b[x2 + 1][y1] -= v;
    b[x1][y2 + 1] -= v;
    b[x2 + 1][y2 + 1] += v;
}
int main()
{
    cin >> n >> m >> q;
    // 构造二维差分数组,和一维差分一样,构造方法有别于传统构造方法
    for (int i = 1; i <= n; ++ i)
        for (int j = 1; j <= m; ++ j)
        {
            int v;
            cin >> v;
            insert(i, j, i, j, v);
        }
    // 进行修改操作
    while (q --)
    {
        int x1, y1, x2, y2, v;
        cin >> x1 >> y1 >> x2 >> y2 >> v;
        insert(x1, y1, x2, y2, v);
    }
    // 求差分的前缀和数组即得原始数组
    for (int i = 1; i <= n; ++ i)
    {
        for (int j = 1; j <= m; ++ j)
        {
            b[i][j] += b[i - 1][j] + b[i][j - 1] - b[i - 1][j - 1];
            cout << b[i][j] << ' ';
        }
        cout << endl;
    }
    return 0;
}
posted @ 2021-01-16 22:30  0x7F  阅读(155)  评论(0编辑  收藏  举报