前缀和、子矩阵的和;差分、差分矩阵

一、前缀和数组

要稍微注意前缀和数组从1开始

#include <iostream>

using namespace std;

const int N = 100010;

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

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);

    for (int i = 1; i <= n; i ++ ) s[i] = s[i - 1] + a[i]; // 前缀和的初始化

    while (m -- )
    {
        int l, r;
        scanf("%d%d", &l, &r);
        printf("%d\n", s[r] - s[l - 1]); // 区间和的计算
    }

    return 0;
}

二、子矩阵的和

S[i,j]表示本身及其左上部分矩阵所有元素的和,再用容斥原理计算某个子矩阵的和

#include <iostream>

using namespace std;

const int N = 1010;

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

int main()
{
    scanf("%d%d%d", &n, &m, &q);

    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= m; j ++ )
            scanf("%d", &s[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];

    while (q -- )
    {
        int x1, y1, x2, y2;
        scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
        printf("%d\n", s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1]);
    }

    return 0;
}

三、差分数组

构造b数组,使得a数组是b数组的前缀和即可,对b数组进行O(1)复杂度操作再对b计算一次前缀和即可得到更新后的a数组,b[l]+c,b[r+1]-c

差分的构造即a[i]-a[i-1]

原数组的构造可以看作是对差分数组[i,i]区间的元素加ai

#include <iostream>

using namespace std;

const int N = 100010;

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()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);

    for (int i = 1; i <= n; i ++ ) insert(i, i, a[i]);

    while (m -- )
    {
        int l, r, c;
        scanf("%d%d%d", &l, &r, &c);
        insert(l, r, c);
    }

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

    for (int i = 1; i <= n; i ++ ) printf("%d ", b[i]);

    return 0;
}

四、差分矩阵

#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[x2 + 1][y1] -= c;
    b[x1][y2 + 1] -= c;
    b[x2 + 1][y2 + 1] += c;
}

int main()
{
    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 ++ )
            insert(i, j, i, j, a[i][j]);

    while (q -- )
    {
        int x1, y1, x2, y2, c;
        cin >> x1 >> y1 >> x2 >> y2 >> c;
        insert(x1, y1, x2, y2, c);
    }

    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];

    for (int i = 1; i <= n; i ++ )
    {
        for (int j = 1; j <= m; j ++ ) printf("%d ", b[i][j]);
        puts("");
    }

    return 0;
}

<think>好的,我现在需要解释前缀和差分这两个概念。首先,我得回忆一下自己对这些的理解,然后组织成用户容易理解的语言。 前缀和,听起来像是前面所有元素的累积到当前的位置。比如一个数组,前缀和数组中的每个元素是原数组从第一个元素到当前位置的总。比如原数组是[1,2,3,4],那前缀和应该是[1,3,6,10]。对吗?需要确认一下定义。是不是这样计算的?对的,没错。前缀和通常用于快速计算区间,比如要算原数组i到j的,只需要前缀和数组的S[j] - S[i-1],这样就O(1)时间了,对吗?比如在算法题中应用广泛,比如处理子数组的问题。 然后是差分差分应该是前缀和的逆过程吗?或者说,差分数组中的每个元素是当前元素前一个元素的差。比如原数组是[1,3,6,10],那差分数组应该是[1,2,3,4]。对吧?因为3-1=2,6-3=3,10-6=4。这样的话,差分数组可以用来快速进行区间增减操作。比如在某个区间加上一个数,只需要在差分数组的两个位置修改,然后通过前缀和恢复原数组,这样可以高效处理多次区间操作。比如原数组要处理m次区间加,每次O(1),最后计算前缀和得到结果数组,总时间复杂度O(n + m),比直接每次O(k)好很多。 接下来,我需要确定这两个概念的应用场景具体例子。前缀和的应用场景比如求子数组的,或者二维情况下的子矩阵。而差分主要用于频繁的区间修改操作,比如批量增减。 用户可能需要了解它们的基本定义、如何构建、以及典型应用。比如前缀和的构建方法是逐个累加,差分则是计算相邻差值。然后举例子说明,比如原数组如何转成前缀和数组,差分数组如何还原原数组,或者如何用差分处理区间加减。 需要注意的点是,前缀和差分通常用于数组,且两者是互逆的操作吗?比如对差分数组求前缀和可以得到原数组,而对原数组求差分再求前缀和也能得到原数组。不过前缀和数组差分数组之间是否有直接的逆关系可能需要更仔细考虑。例如,原数组的前缀和数组的差分应该是原数组本身吗?比如原数组是a,前缀和数组是s,那么s的差分应该是a,没错。反过来,原数组的差分数组d,再求前缀和,应该得到原数组。对吧?比如原数组是[1,2,3,4],差分数组是[1,1,1,1],前缀和数组是[1,3,6,10],但差分数组的前缀和应该是原数组,对吗?是的,比如差分数组d的前缀和就是1, 1+1=2, 1+1+1=3,这样好像不对。哦,这里可能哪里出错了? 比如原数组是a=[a1,a2,a3,...,an],差分数组d=[a1, a2 -a1, a3 -a2, ..., an -a(n-1)]。那么前缀和数组s_d的第一个元素是a1,第二个是a1 + (a2 -a1)=a2,第三个是a2 + (a3 -a2)=a3,依此类推,所以差分数组的前缀和数组就是原数组。所以是的,差分前缀和是互逆的运算。那前缀和数组的差分数组就是原数组。所以这个关系是正确的。 那在应用上,差分数组用于快速修改区间,比如给区间[l, r]每个元素加c,那么差分数组d[l] += c,d[r+1] -=c,之后求前缀和就能得到修改后的原数组。这个例子需要详细说明,比如原数组是a,差分数组d。当对a的某个区间加c,差分数组的操作只需要两次修改,然后求前缀和就能得到正确的数组。这样可以高效处理多次区间操作。 在解释这两个概念的时候,需要注意它们的定义、构建方法、相互关系典型应用。还要举例子说明,比如计算前缀和数组的具体步骤,差分的构建步骤,以及如何利用它们解决问题。 另外,可能需要提到它们在算法中的重要性,比如在解决某些问题时可以优化时间复杂度。例如,用前缀和将区间的查询从O(n)降到O(1),用差分将区间修改从O(n)降到O(1)每次操作。 总结一下,我需要分步骤解释前缀和差分,每个部分包括定义、如何构建、例子、应用场景,以及两者的关系。确保语言清晰,结构合理,例子准确。可能需要用Latex公式来表示定义,比如前缀和的数学表达式,差分的表达式。例如,前缀和数组s[i] = a[1] + a[2] + ... +a[i],而差分数组d[i] = a[i] -a[i-1],其中d[0] = a[0]。 注意,在中文中,数组通常从0开始还是1开始?这个问题可能需要根据习惯处理。比如在算法中,有时会为了方便处理,将数组索引从1开始,这样差分前缀和的公式可以更简洁,比如不用处理i=0的情况。所以在例子中可能采用从1开始索引的方式,或者明确说明索引的起始点。 此外,可能需要提到二维前缀和二维差分,但用户的问题可能只关注一维情况。所以暂时先专注于一维的情况,如果用户有进一步的问题再扩展。 现在,把这些思考整理成一个结构化的回答,分点说明,确保每个概念都有定义、例子、应用,并且说明两者的关系。同时使用Latex公式符合用户之前的格式要求,比如行内公式用$,独立公式用$$,并且正确使用语法。</think>### 前缀和差分的分步解释 --- #### 一、前缀和(Prefix Sum) **定义** 前缀和是指将数组中**从第一个元素到当前元素**的累加,构成一个新的数组。 设原数组为 $a[1], a[2], \dots, a[n]$,则前缀和数组 $s$ 定义为: $$ s[i] = a[1] + a[2] + \cdots + a[i] \quad (1 \leq i \leq n) $$ **构建方法** - 初始化 $s[0] = 0$(方便计算区间)。 - 递推公式: $$ s[i] = s[i-1] + a[i] \quad (1 \leq i \leq n) $$ **例子** 原数组:$a = [1, 2, 3, 4]$ 前缀和数组: $$ s = [1, 3, 6, 10] $$ **应用场景** 1. **快速计算区间** 若需计算原数组区间 $[l, r]$ 的,只需: $$ \text{区间} = s[r] - s[l-1] $$ *示例*:计算 $a[2 \text{到}4]$ 的(即 $2+3+4=9$),可通过 $s[4]-s[1] = 10 -1=9$ 直接得出。 时间复杂度从 $O(n)$ 优化为 $O(1)$。 2. **二维扩展** 在矩阵中求子矩阵(需二维前缀和)。 --- #### 二、差分(Difference Array) **定义** 差分前缀和的逆操作,记录数组中**相邻元素的差值**。 设原数组为 $a[1], a[2], \dots, a[n]$,则差分数组 $d$ 定义为: $$ d[i] = a[i] - a[i-1] \quad (2 \leq i \leq n), \quad d[1] = a[1] $$ **构建方法** - 初始化 $d[1] = a[1]$。 - 递推公式: $$ d[i] = a[i] - a[i-1] \quad (2 \leq i \leq n) $$ **例子** 原数组:$a = [1, 3, 6, 10]$ 差分数组: $$ d = [1, 2, 3, 4] $$ **应用场景** 1. **快速进行区间增减操作** 若需将原数组区间 $[l, r]$ 的每个元素加 $c$,只需: $$ d[l] \text{ += } c, \quad d[r+1] \text{ -= } c \quad (\text{若存在 } r+1 \leq n) $$ *示例*:将 $a[2 \text{到}3]$ 的元素加 $2$: - 修改差分数组:$d[2] \text{ += }2$,$d[4] \text{ -= }2$。 - 重新计算前缀和:原数组变为 $[1, 5, 8, 10]$。 时间复杂度从 $O(n)$ 优化为 $O(1)$ 每次操作。 --- #### 三、前缀和差分的关系 1. **互为逆运算** - 对原数组 $a$ 求前缀和得到 $s$,再对 $s$ 求差分可还原 $a$。 - 对原数组 $a$ 求差分得到 $d$,再对 $d$ 求前缀和也可还原 $a$。 2. **操作流程示例** - *原数组 → 差分前缀和 → 原数组* - *原数组 → 前缀和差分 → 原数组* --- #### 四、典型问题对比 | **场景** | 前缀和 | 差分 | |------------------|--------------------------------|-------------------------------| | **核心功能** | 快速计算区间 | 快速修改区间值 | | **时间复杂度** | 预处理 $O(n)$,查询 $O(1)$ | 预处理 $O(n)$,修改 $O(1)$ | | **适用问题** | 子数组、统计频率 | 区间增减、批量更新 | --- #### 五、实际应用案例 1. **前缀和** - *LeetCode 560. 为 K 的子数组*:通过前缀和快速统计满足条件的子数组数量。 - *股票统计*:计算某段时间内的累计收益。 2. **差分** - *航班预订统计*(LeetCode 1109):处理多个区间增减操作后输出最终结果。 - *温度区间调整*:对某段时间的温度统一增加或减少。 --- ### 总结 - **前缀和**是“提前累加”,用于**高效求**; - **差分**是“记录变化”,用于**高效修改**。 两者结合可解决大量数组操作问题,是算法竞赛工程中的常用技巧。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值