首页 > 其他分享 >前缀和与差分

前缀和与差分

时间:2022-12-22 19:02:20浏览次数:39  
标签:y2 前缀 int 差分 x2 数组 y1 x1

1.一维前缀和

image
前缀和是指某序列的前n项和,可以把它理解为数学上的数列的前n项和,而差分可以看成前缀和的逆运算。合理的使用前缀和与差分,可以将某些复杂的问题简单化。

有一个长度为n的序列,求序列中r到l的和。如果数据很大采用循环遍历肯定会TLE,所以就要采用前缀和的方式来求解

#include<iostream>
using namespace std;
const int N=100010;
int a[N],s[N];

int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        s[i]=s[i-1]+a[i];	//预处理先求出前缀和数组
    }
    
    while(m--)
    {
        int r,l;
        scanf("%d%d",&l,&r);
        printf("%d\n",s[r]-s[l-1]);
    }
    
    return 0;
}

1.1二维前缀和

image
输入一个 n行 m列的整数矩阵,再输入 q个询问,每个询问包含四个整数 x1,y1,x2,y2,表示一个子矩阵的左上角坐标和右下角坐标。
对于每个询问输出子矩阵中所有数的和。


#include <iostream>

using namespace std;

const int N = 1010;

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

int main() {
    int n, m, q;
    cin >> n >> m >> q;

    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
		{
            scanf("%d", &a[i][j]);
            s[i][j] = s[i][j - 1] + s[i - 1][j] - s[i - 1][j - 1] + a[i][j]; // 求前缀和
        }

    while (q--) 
	{
        int x1,y1,x2,y2;
        scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
        // 算子矩阵的和
        printf("%d\n", s[x2][y2] - s[x2][y1 - 1] - s[x1 - 1][y2] + s[x1 - 1][y1 - 1]); 
    }

    return 0;
}

计算子矩阵(x1,y1),(x2,y2)时不能要做一个-1处理,不能将(x1,y1)也减掉。
image
将坐标代入样例中即可一目了然

2差分

image
差分数组:

首先给定一个原数组a:a[1], a[2], a[3],,,,,, a[n];

然后我们构造一个数组b : b[1], b[2], b[3],,,,,, b[i];

使得 a[i] = b[1] + b[2] + b[3] + ,,,,,, + b[i]

也就是说,a数组是b数组的前缀和数组,反过来我们把b数组叫做a数组的差分数组。换句话说,每一个a[i]都是b数组中从头开始的一段区间和。

输入一个长度为 n
的整数序列。

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

#include<iostream>
using namespace std;
const int N = 1e5 + 10;
int a[N], b[N];
int main()
{
    int n, m;
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &a[i]);
        b[i] = a[i] - a[i - 1];      //构建差分数组	a[i]=b[i]+a[i-1];
    }
    int l, r, c;
    while (m--)
    {
        scanf("%d%d%d", &l, &r, &c);
        b[l] += c;     //将序列中[l, r]之间的每个数都加上c
        b[r + 1] -= c;
    }
    for (int i = 1; i <= n; i++)
    {
        a[i] = b[i] + a[i - 1];    //前缀和运算。因为b数组做过更改,所以直接做一遍前缀和运算即可。
        printf("%d ", a[i]);
    }
    return 0;
}

2.2二维差分数组

image

输入一个n行m列的整数矩阵,再输入q个操作,每个操作包含五个整数x1, y1, x2, y2, c,其中(x1, y1)和(x2, y2)表示一个子矩阵的左上角坐标和右下角坐标。每个操作都要将选中的子矩阵中的每个元素的值加上c。请你将进行完所有操作后的矩阵输出。

输入格式
第一行包含整数n, m, q。
接下来n行,每行包含m个整数,表示整数矩阵。
接下来q行,每行包含5个整数x1, y1, x2, y2, c,表示一个操作。

#include<iostream>
#include<cstdio>
using namespace std;
const int N = 1e3 + 10;
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()
{
    int n, m, q;
    cin >> n >> m >> q;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            cin >> 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]);
        }
        printf("\n");
    }
    return 0;
}

b[x1][y1] += c ; 对应图1 ,让整个a数组中蓝色矩形面积的元素都加上了c。

b[x1,][y2 + 1] -= c ; 对应图2 ,让整个a数组中绿色矩形面积的元素再减去c,使其内元素不发生改变。

b[x2 + 1][y1] -= c ; 对应图3 ,让整个a数组中紫色矩形面积的元素再减去c,使其内元素不发生改变。

b[x2 + 1][y2 + 1] += c; 对应图4,让整个a数组中红色矩形面积的元素再加上c,红色内的相当于被减了两次,再加上一次c,才能使其恢复。
image

我们可以先假想a数组为空,那么b数组一开始也为空,但是实际上a数组并不为空,因此我们每次让以(i,j)为左上角到以(i,j)为右下角面积内元素(其实就是一个小方格的面积)去插入 c = a[i][j] ,等价于原数组a中(i,j) 到(i,j)范围内 加上了 a[i][j] ,因此执行 n*m次插入操作,就成功构建了差分b数组

标签:y2,前缀,int,差分,x2,数组,y1,x1
From: https://www.cnblogs.com/onginer/p/16999393.html

相关文章

  • P4231 三步必杀——二阶差分
    问题摘要:\(N\)个柱子排成一排,一开始每个柱子损伤度为0。接下来勇仪会进行\(M\)次攻击,每次攻击可以用4个参数\(l\),\(r\),\(s\),\(e\)来描述:表示这次攻击作用范围为第\(l......
  • 强化学习(六):时序差分方法
    强化学习(六):时序差分方法  时序差分(TD)方法结合了动态规划与蒙特卡洛的思想,其可以像蒙特卡洛方法一样直接从智能体与环境互动的经验中学习,而不需要知道环境的模型,其又可以像......
  • 我说MySQL联合索引遵循最左前缀匹配原则,面试官让我回去等通知
    携手创作,共同成长!这是我参与「掘金日新计划·8月更文挑战」的第6天,点击查看活动详情面试官:我看你的简历上写着精通MySQL,问你个简单的问题,MySQL联合索引有什么特性?心......
  • 组合数前缀和
    众所周知有经典问题:\(T\)组询问\(\sum_{i=0}^m\binom{n}{i}\)。听说戴老师一年多前就有个\(n\log^2n\)的做法,感觉很厉害,但是搜不到做法,也想尝试自己推推。今天受到......
  • 797差分
    原题链接定义差分数组b[],其中\(b[i]=a[i]-a[i-1]\)\(a_{x}=\sum_{i=1}^{x}b_{i}\)更改\(a[l~r]\),只要更改\(b[l-1]\)和\(b[r]\)即可,最后要对\(b[]\)数组做......
  • 795前缀和,线段树,树状数组
    题目描述输入一个长度为\(n\)的整数序列。接下来再输入\(m\)个询问,每个询问输入一对\(l,r\)。对于每个询问,输出原序列中从第\(l\)个数到第\(r\)个数的和。输......
  • 前缀树(Tire)—Python
    核心思想空间换时间,是一种用于快速减速的多叉树结构,利用字符串的公共前缀来降低时间优缺点:优点:查询效率高,减少字符比较缺点:内存消耗较大每次都会从头向下一直到字符串......
  • 前缀和【模板题】
    前缀和输入一个长度为n的整数序列。接下来再输入m个询问,每个询问输入一对l,r。对于每个询问,输出原序列中从第l个数到第r个数的和。输入格式第一行包含两个整......
  • 新增自定义前缀
    新增自定义前缀1.设置->自定义项2.选择发布者3.新建4.填写信息项,保存即可......
  • 二次差分维护
    原理类似,用线段树来维护区间求和,只是一个是一次差分的值,一个是二次差分的值P1438无聊的数列每次加上等差数列,单点查询#include<bits/stdc++.h>usingnamespacestd;......