差分——(1)一维差分

差分,是一种和前缀和相对的策略。

差分概念

对于一个数列 a_{i}我们需要维护的数据是“相邻两个数之差”。这种策略是,令p_{i}=\bigtriangleup a_{i} = a_{i}-a_{i-1},即相邻两数的差。我们称数列 p_{i} 为数列 a_{i} 的差分数列。

应用

它可以维护多次对序列的一个区间加上一个数,并在最后询问某一位的数或是多次询问某一位的数。譬如使 [l, r] 每个数加上一个 k,就是 p_{l}\leftarrow p_{l}+k, p_{r+1}\leftarrow p_{r+1}-k,最后做一遍前缀和。

就是对这个差分数列 p_{i} 做一遍前缀和就得到了原来的数列 a_{i} ,即 a_{i} 相当于 p_{[1,\ i]} 这个前缀和。

证明如下:

\sum _{i=1}^{n}a[i]=a[1]+a[2]+a[3]+...+a[n]\\=(p[1])+(p[1]+p[2])+(p[1]+p[2]+p[3])+...+(p[1]+p[2]+p[3]+...+p[n])\\=n*p[1]+(n-1)*p[2]+(n-2)*p[3]+...+2*p[n-1]+1*p[n]\\=n*(p[1]+p[2]+...p[n])-(0*p[1]+1*p[2]+...(n-1)*p(n))

这样,我们可以发现一个规律,即第二个多项式的系数为 i-1。我们用 p_{2}[i] 来维护这个数组,那么 p_{2}[i]=(i-1)*p[i],并且在修改时候维护 p_{2}[i] 数组,即 p[l] +(l-1)*s,p[r+1]-(r+1-l)*s 只后便有了公式 \sum _{i=1}^{n}a[i]=n*\sum _{i=1}^{n}p[i]-\sum _{i=1}^{n}p_{2}[i]

举例

目前 a=\left \{ 1,2,3,3,3,3 \right \},则对应的差分数列 p=\left \{ 1,1,1,0,0,0 \right \}

给 [3, 5] 加上首项为2、公差为 1 的等差数列,及 \left \{ 2,3,4 \right \}。那么原数列变为 a=\left \{ 1,2,5,6,7,3 \right \},对差分数列变为 p=\left \{ 1,1,3,1,1,-4 \right \}

说明 a 区间加等差,相当于 p 区间加常数、端点单点修改。也就是区间加自然是给 p_{[l+1, r]} 加上 d。

模板题

题目链接

我的OJ,http://47.110.135.197/problem.php?id=5226

题目描述

输入一个长度为 n 的整数序列。
接下来输入 m 个操作,每个操作包含三个整数 l,  r,  c,表示将序列中 [l, r] 之间的每个数加上 c。
请你输出进行完所有操作后的序列。

输入

第一行包含两个整数 n 和 m。
第二行包含 n 个整数,表示整数序列。
接下来 m 行,每行包含三个整数 l,r,c,表示一个操作。

输出

共一行,包含 n 个整数,表示最终序列。

样例输入

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

样例输出

3 4 5 3 4 2

分析

这是一个一维差分的模板题。

数据分析

下面我们根据样例输入来分析一下,样例输出是如何得到的。

初始状态的差分数组 diff 为 1 1 0 -1 1 -1。注意第一个下标为 1。

第一次操作为 1 3 1,那么就是 diff[1] = diff[1]+1,diff[4] = diff[4]-1,得到差分数组 diff 变为 2 1 0 -2 1 -1。

第二次操作为 3 5 1,那么就是 diff[3] = diff[3]+1,diff[6] = diff[6]-1,得到差分数组 diff 变为 2 1 1 -2 1 -2。

第三次操作为 1 6 1,那么就是 diff[1] = diff[1]+1,diff[7] = diff[7]-1,得到差分数组 diff 变为 3 1 1 -2 1 -2 -1。

因此,最终的原始数组如下:

a[1] = diff[0]+diff[1] = 3

a[2] = diff[0]+diff[1]+diff[2]  = 4

a[3] = diff[0]+diff[1]+diff[2]+diff[3]  = 5

a[4] = diff[0]+diff[1]+diff[2]+diff[3]+diff[4]  = 3

a[5] = diff[0]+diff[1]+diff[2]+diff[3]+diff[4]+diff[5]  = 4

a[6] = diff[0]+diff[1]+diff[2]+diff[3]+diff[4]+diff[5]+diff[6]  = 2

数据范围

从题目中知道,n 的最大值为 100000,因此我们定义数组为 100004。

数组的每个数范围为 [-1000, 1000],c 的范围为 [-1000, 1000],操作数 m 最大值为 100000。因此我们可以计算出,经过 m 次操作后,最大的数据为 1000+1000*100000 = 10^8+1000,在 int 的表示范围内。同理最小的数据将是 -1000+(-1000*100000)=-10^8-1000,也在 int 的表示范围内。

AC代码

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 1e5+6;
int data[MAXN] = {};
int diff[MAXN] = {};

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

    int i;
    for (i=1; i<=n; i++) {
        scanf("%d", &data[i]);
        diff[i] = data[i] - data[i-1];
    }

    for (i=0; i<m; i++) {
        int l, r, c;
        scanf("%d%d%d", &l, &r, &c);
        diff[l] += c;
        diff[r+1] -= c;
    }

    //输出
    int ans=diff[0];
    for (i=1; i<=n; i++) {
        ans += diff[i];
        printf("%d ", ans);
    }
    printf("\n");

    return 0;
}
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力的老周

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值