前缀与差分

前缀与差分

1. 算法分析

1.1 前缀和

定义
s[n] = ∑ i = 1 n a [ i ] \sum_{i=1}^na[i] i=1na[i]

递推关系
s[i] = a[i] + s[i - 1]

区间求和
∑ i = l r a [ i ] = s [ r ] − s [ l − 1 ] \sum_{i=l}^ra[i] = s[r] - s[l - 1] i=lra[i]=s[r]s[l1]

1.2 差分

定义
存在两个数组a(a1, a2, a3,…, an)和b(b1, b2, … ,bn)
如果ai = b1 + b2 + … + bi
那么b称为a的差分(比如: b1 = a1, b2 = a2 - a1)

作用

  1. 区间增加->单点修改:当a[l]~a[r]这个区间内元素全加上c时,只需要对b[l] + c, b[r+1] - c即可,因为:b[l] + c:让a[l]往后的元素全部加上c。b[r+1]-c:防止a[r+1]开始往后的元素加上c
  2. 一开始给ai赋值时,可以看成是给a[i]~a[i]这段全部加上c

构造
差分的构造可以选择: b[i] = a[i] - a[i - 1]

变形
差分可以维护a的变化值,即b[i] = Δai = $ \sum_{i=1}^nb[i] $

2. 板子

2.1 前缀和

2.1.1 一维前缀和
#include <bits/stdc++.h>

using namespace std;

int const N = 1e5 + 10;
int s[N], a[N];

int main() {
    int n, k;
    cin >> n >> k;
    // 计算前缀和
    for (int i = 1; i <= n ; ++i) {
        scanf("%d", &a[i]);
        s[i] = s[i - 1] + a[i];
    }
    
    // 使用前缀和
    for (int i = 0; i < k ; ++i) {
        int l, r;
        scanf("%d %d", &l, &r);
        printf("%d\n", s[r] - s[l -1]);
    }
    return 0;
}
2.1.2 二维前缀和
#include <bits/stdc++.h>

using namespace std;

int const N = 1e3 + 10;
int s[N][N], a[N][N];

int main() {
    int n, m, k;
    cin >> n >> m >> k;
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= m; ++j) {
            scanf("%d", &a[i][j]);
            s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j];  // 前缀和在二维的情况
        }
    for (int i = 0; i < k; ++i) {
        int l1, r1, l2, r2;
        scanf("%d %d %d %d", &l1 ,&r1, &l2, &r2);
        printf("%d\n", s[l2][r2] - s[l2][r1 - 1] - s[l1 - 1][r2] + s[l1 - 1][r1 - 1]);  // 打印结果
    }
    return 0;
}

2.2 差分

2.2.1 一维差分
#include <bits/stdc++.h>

using namespace std;

int const N = 1e5 + 10;
int b[N], n, m, a[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) scanf("%d", &a[i]);  // 读入原数组
    for (int i = 1, l, r, c; i <= m; ++i) {  // 不断读入区间增加
        scanf("%d %d %d", &l, &r, &c);
        insert(l, r, c);  
    }
    for (int i = 1; i <= n; ++i) {
        b[i] += b[i - 1];  // 计算a[i]的变化值
        printf("%d ", a[i] + b[i]);
    }
    return 0;
}
2.2.1 二维差分
#include <bits/stdc++.h>

using namespace std;

int const N = 1e3 + 10;
int a[N][N], b[N][N];
int n, m, q;

// 区间增加(二维情况)--和前缀和情况不一样
void insert(int l1, int r1, int l2, int r2, int c) {
    b[l1][r1] += c;
    b[l2 + 1][r2 + 1] += c;
    b[l2 + 1][r1] -= c;
    b[l1][r2 + 1] -= c;
}

int main() {
    cin >> 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, l1, r1, l2, r2, c; i <= q; ++i) {
        scanf("%d%d%d%d%d", &l1, &r1, &l2, &r2, &c);  // 区间增加
        insert(l1, r1, l2, r2, 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];  // 前缀求和,计算变化量
            printf("%d ", a[i][j] + b[i][j]);  // 计算变化后的a[i][j]
        }
        printf("\n");
    }
    return 0;
}

3. 板子

3.1 前缀和

acwing1230. K倍区间
题意: 给定一个长度为 N 的数列,A1,A2,…AN,如果其中一段连续的子序列 Ai,Ai+1,…Aj 之和是 K 的倍数,我们就称这个区间 [i,j] 是 K 倍区间。求出数列中总共有多少个 K 倍区间。
题意: (sum[r] - sum[l - 1]) % k == 0,则sum[r] % k == sum[l - 1] % k。因此只需要找到前缀和%k相等的个数即可,同时要注意l<=r,则l - 1 < r, 同时记得加上左边界不选的情况。
代码:

#include <bits/stdc++.h>

using namespace std;

int const N = 1e5 + 10;
long long sum[N], a[N], n, k, cnt[N];

int main() {
    cin >> n >> k;
    
    long long res = 0;
    cnt[0] = 1;  // 因为存在sum[k],即只需要右边界,不需要左边界的情况
    for (int i = 1; i <= n; ++i) {
        scanf("%d", &a[i]);
        sum[i] = (sum[i - 1] + a[i]) % k;
        res += cnt[sum[i]];
        cnt[sum[i]]++;
    }
    cout << res << endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值