差分数组模板

差分

差分构造方式
原数组A,差分数组B,数组范围【1,n】
B[1] = A[1] - A[0] = A[1]
B[i] = A[i] - A[i-1]
差分特点
B数组的前缀和 <> A数组的元素值
SUM{B[1~i ]} = A[i]
A 数组的区间加 <
> B数组的单点修改
A[L]~A[R] 均加上c
等价于B【L] += c, B[R+1] -= c

题目描述

有 nn 个人买东西同一个商品,第 ii 个人认为合理的价格区间 [l_i,r_i]表示如果价格低于 l嫌便宜,如果价格高于 r 嫌贵。

请你设计商品的价格,使在满足尽可能多的人认为价格合理的条件下,商品价格最高。具体来说,你需要输出最终商品的定价与认为价格合理的人数。

输入描述

第一行输入一个整数 n(1<= n <=10^6)
接下来 n 行,第 i 行输入两个数 l_i, r_il (1 <= l_i <= r_i <=10^6)表示第 ii个人认为的价格合理的区间。

输出描述

输出一行两个整数,以一个空格分割,分别表示最终商品的定价与认为价格合理的人数。

输入样例

5
1 3
2 4
2 3
3 4
4 5

输出样例

3 4

解题思路

差分,构造差分数组b[i],当有一个合理区间,l r,则在数组a上从a[l]到a[r]每一个都加一,即b[l]+1,b[r+1]+1,最终统计值最大得a[i],从最大值中找最大的i

#include<bits/stdc++.h>
#define N 1000010
using namespace std;
int b[N] = {0};
int a[N] = {0};
int main()
{

    //freopen("a.in","r",stdin);
    //freopen("a.out","w",stdout);
    int n;
    cin>>n;
    for(int i = 0;i < n;i++)
    {
        int l,r;
        cin>>l>>r;
        b[l]++;
        b[r+1]--;
    }
    int num = 0;
    int price;
    for(int i=1;i <= n;i++)
    {
        a[i] = a[i-1] + b[i];
        if(a[i] >= num)
        {
            num = a[i];
            price = i;
        }
    }
    cout<<price<<" "<<num<<endl;
    return 0;
}

关于差分的思考

将原数组看作是前缀和数组,通过辅助数组(差分数组)b,对数组b进行前缀,还原原数组。

因为只需要修改差分数组的单点的值,就能对前缀中的许多值产生影响:
比如,b:1 2 3 4 5 ,原数组(前缀和)1 3 6 10 15
修改后 b:2 2 3 4 5 ,原数组从第一个数开始,每一个数的值都加1,
如果只需要将第1到4个数的值加1,对差分数组的第五个数减1,来抵消影响,
即 2 2 3 4 4 原数组变为:2 4 7 11 15

二维差分

原先二维数组最为二位前缀和,差分数公式推导为
p [i] [j] = a[i][j] - a[i-1][j] - a[i] [j-1] + a[i-1][j-1]

从差分数组还原到原矩阵:
p[i][j] = p[i][j] + p[ i -1][j] + p[i][j-1] - p[i-1][j-1]

举例:
有一个矩阵a,如下

  • 1 2 4 3
  • 5 1 2 4
  • 6 3 5 9

差分矩阵p,如下

  • 1 1 2 -1
  • 4 -5 -1 3
  • 1 1 1 2

二维区间修改转化为单点修改

在差分数组中,修改(x1,y1)的值 + c,求前缀之后,黄色的区域都会受到影响
修改(x1,y2) -c (x2,y1) -c,蓝色的部分抵消影响,绿色的部分求前缀多减了c,(x2,y2)+c抵消影响

#include <bits/stdc++.h>
using namespace std;
 
const int MAXN = 1e3+6;
const int MAXM = 1e3+6;
int a[MAXN][MAXM] = {};
int diff[MAXN][MAXM] = {};
 
int main() {
    int n,m,q;
    scanf("%d%d%d", &n, &m, &q);
 
    int i, j;
    for (i=1; i<=n; i++) {
        for (j=1; j<=m; j++) {
            scanf("%d", &a[i][j]);
            diff[i][j] = a[i][j]-a[i-1][j]-a[i][j-1]+a[i-1][j-1];
        }
    }
 
    for (i=0; i<q; i++) {
        int x1, y1, x2, y2, c;
        scanf("%d%d%d%d%d", &x1, &y1, &x2, &y2, &c);
        diff[x1][y1] += c;
        diff[x1][y2+1] -=c;
        diff[x2+1][y1] -=c;
        diff[x2+1][y2+1] += c;
    }
 
    for (i=1; i<=n; i++) {
        for (j=1; j<=m; j++) {
            diff[i][j] += diff[i-1][j]+diff[i][j-1]-diff[i-1][j-1];
            printf("%d ", diff[i][j]);
        }
        printf("\n");
    }
 
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值