前缀和

article/2025/10/2 0:49:47

【前缀和】

什么是前缀和?前缀和是一个数组的某项下标之前(包括此项元素)的所有数组元素的和。 

设b[]为前缀和数组,a[]为原数组,根据这句话可以得到前缀和的定义式和递推式:

 定义式递推式
一维前缀和 b[i]=\sum_{j=0}^{i}a[j]b[i]=b[i-1]+a[i]
二维前缀和b[x][y]=\sum_{i=0}^{x}\sum_{j=0}^{y}a[i][j]b[x][y]=b[x-1][y]+b[x][y-1]-b[x-1][y-1]+a[x][y]

 

【一维前缀和】

根据上面的定义,我们可以很容易得到 sum[i] = sum[i-1] + a[i]   这样就可以得到前i个数的和

根据上述表达式我们可以以O(1)求出区间[i,j]的区间和     sum[i,j]=b[j]-b[i-1]

 

 

代码:

#include <cstdio>
#include <string>
#include <iostream>
#include <algorithm>
#include <cstdbool>
#include <string.h>
#include <math.h>using namespace std;int main()
{int a[100005] = {0};int b[100005] = {0};int n;cin >> n;for (int i=1;i<=n;i++){cin >> a[i];b[i] = b[i-1] + a[i];}int t;cin >> t;while (t--){int l,r;int sum = 0;cin >> l >> r;sum = b[r] - b[l-1];printf("%d\n",sum);}return 0;
}

 

这道题改动了一下,如果可以找到,那么输出所有的符合条件的区间

这道题的思路其实不难理解:

例如:

[1,a] 和 [1,b]   (b > a)

如果要满足条件那么  (b-a) % m == x

->           (b-x) % m == a

我们对它的前缀和对m模(假设为 t )并存储

vis[(t-x+m)%m] 存在,则说明可以找到这样的一个子区间

但是由于我这里要输出所有的符合条件的区间,所以我需要去存储数组索引 (也就是区间的左边界)

一种是利用二维数组:

#include <cstdio>
#include <string>
#include <iostream>
#include <algorithm>
#include <cstdbool>
#include <string.h>
#include <math.h>using namespace std;int main()
{int n,m,x;cin >> n >> m >> x;int ss[105][105];for (int i=0;i<105;i++){for (int j=0;j<105;j++){ss[i][j] = -1;}}int a[105];int vis[105] = {0};int pre[105];pre[0] = 0;ss[0][0] = 0;int l,r;for (int i=1;i<=n;i++) {cin >> a[i];}for (int i=1;i<=n;i++){pre[i] = (pre[i-1] + a[i]) % m;vis[pre[i]]++;int xh = (pre[i] - x) % m;if (xh < 0)xh += m;if (vis[xh] != 0){for (int j=0;j<105;j++){if (ss[xh][j] != -1){l = ss[xh][j] + 1;r = i;cout << l << " " << r << endl;} elsebreak;}}for (int j=0;j<105;j++){if (ss[pre[i]][j] == -1){ss[pre[i]][j] = i;break;}elsecontinue;}}return 0;
}

一种是利用vector:

#include <cstdio>
#include <string>
#include <iostream>
#include <algorithm>
#include <cstdbool>
#include <string.h>
#include <math.h>
#include <vector>using namespace std;int main()
{int n;int m;int x;scanf("%d%d%d", &n, &m, &x);int a[100];for (int i = 1; i <= n; ++i) {scanf("%d", &a[i]);}int pre[100];pre[0] = 0;vector<int> preffix[100];preffix[0].push_back(0);for (int i = 1; i <= n; ++i) {pre[i] = (pre[i-1] + a[i]) % m;int xh = (pre[i] - x) % m;if (xh < 0) { xh += m; }if (!preffix[xh].empty()) {for (int pre_index: preffix[xh]) {printf("%d:%d\n", pre_index + 1, i);}}preffix[pre[i]].push_back(i);}return 0;
}

 

直接在问题二的基础上修改就可以了

 

 

思路:

因为是除去区间[l,r] 那么我就分两个前缀和(一个从前往后求,一个从后往前求)

pre[l-1] 和 suf[r+1] 求这俩的gcd就可以了

 

代码:

#include <cstdio>
#include <string>
#include <iostream>
#include <algorithm>
#include <cstdbool>
#include <string.h>
#include <math.h>
#include <vector>using namespace std;int pre[10005];
int suf[10005];
int a[100005];
int n;
int gcd(int a,int b)
{return b ? gcd(b, a % b) : a;
}void presolve()
{pre[0] = 0;for(int i = 1; i <= n; i++) {pre[i] = gcd(pre[i - 1], a[i]);}suf[n + 1] = 0;for(int i = n; i >= 1; i--) {suf[i] = gcd(suf[i + 1], a[i]);}
}int query(int l,int r)
{return gcd(pre[l-1],suf[r+1]);
}int main()
{cin >> n;for (int i=1;i<=n;i++)cin >> a[i];presolve();int l,r;cin >> l >> r;int n_gcd = query(l,r);printf("%d\n",n_gcd);return 0;
}

 

【问题五】

给你一串长度为n的数列a1,a2,a3......an,要求对a[L]~a[R]进行m次操作:

操作一:将a[L]~a[R]内的元素都加上P

操作二:将a[L]~a[R]内的元素都减去P

最后再给出一个询问求a[L]-a[R]内的元素之和?

 

 这里讲差分! 

它可以用来专门解决这种修改值的问题!

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+9;
int a[maxn],b[maxn];
int main(){int i,j,k,n,m,p;cin>>n>>m;for(i=1;i<=n;i++){cin>>a[i];}for(i=1;i<=m;i++){int L,R,t;cin>>t>>L>>R>>p;if(t==1){b[L]+=p;b[R+1]-=p; //仔细想想为什么b[R+1]要减去p}else{b[L]-=p;b[R+1]+=p;}}int add=0;for(i=1;i<=n;i++){add+=b[i];a[i]+=a[i-1]+add;}int x,y;cin>>x>>y;cout<<a[y]-a[x-1]<<endl;
}

为什么操作一时b[R+1]要减去p,很简单,因为操作一我只需对[L,R]区间里的数加p,[R+1,n]这个区间里的数没必要加p,所以需要减掉p。

 

 

【二维前缀和】

DP[i][j]表示(1,1)这个点与(i,j)这个点两个点分别为左上角和右下角所组成的矩阵内的数的和,好好想一下状态转移方程,DP[i][j]=DP[i-1][j]+DP[i][j-1]-DP[i-1][j-1]+map[i][j],怎么来的呢?我们画一下图就知道了。

这张图就知道了(i,j)可以由(i-1,j)和(i,j-1)两块构成,不过要注意两个点,1、有一块矩阵我们重复加了,也就是(i-1,j-1)这一块,所以我们要减去它。2、我们这个矩阵是不完整的,由图可知我们还有一块深蓝色的没有加,也就是(i,j)这一点,所以我们要再加上map[i][j]也就是题目给出的矩阵中这一格的数。
 

如果我们定义[x1,y1] 为所求矩阵左上角 [x2,y2] 为所求矩阵右下角   如何得到它们所围成矩阵的总和呢?

我们可以通过DP[x2][y2]来计算,我们通过图可以发现这个距离我们要的还差红色的部分看看怎么表示红色部分?我们可以分割成两块,分别是DP[x1][y2]和DP[x2][y1]我们发现有一块重复减了,所以我们再加上它即DP[x1][y1],有一点注意,因为画图和定义原因我们发现边界好像不对,我们来看看,我们定义的状态是整个矩阵包括边的和,而我们要求的也是要包括边的,所以我们要再改一下,把DP[x1][y2]和DP[x2][y1]和DP[x1][y1]分别改成DP[x1-1][y2]和DP[x2][y1-1]和DP[x1-1][y1-1]这样一减我们就可以得到自己想要的答案,整理可得公式,DP[x2][y2]-DP[x1-1][y2]-DP[x2][y1-1]+DP[x1-1][y1-1]这样我们就可以做到O(1)之内查询
 

求前缀和的代码:

#include<iostream>
#include<cstring>
using namespace std;
int dp[2000][2000],map[2000][2000];
int main()
{int m,n,k;//所给的矩阵是n*m的,有k组查询 cin >>n>>m>>k;for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)cin >>map[i][j];memset(dp,0,sizeof(dp));for(int i=1;i<=n;i++)//预处理一波 for(int j=1;j<=m;j++)dp[i][j]=dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1]+map[i][j];for(int i=1;i<=k;i++)//接受查询 {int x1,x2,y1,y2;cin >>x1>>y1>>x2>>y2;cout <<(dp[x2][y2]+dp[x1-1][y1-1]-dp[x1-1][y2]-dp[x2][y1-1])<<endl;//O(1)查询 }return 0;
} 

 


http://chatgpt.dhexx.cn/article/Gvs0JbjB.shtml

相关文章

Java前缀和算法

一.什么是前缀和算法 通俗来讲&#xff0c;前缀和算法就是使用一个新数组来储存原数组中前n-1个元素的和&#xff08;如果新数组的当前元素的下标为n&#xff0c;计算当前元素的值为原数组中从0到n-1下标数组元素的和&#xff09;&#xff0c;可能这样讲起来有点抽象&#xff0…

基础算法——前缀和详解

秋名山码民的主页 &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f64f;作者水平很有限&#xff0c;如果发现错误&#xff0c;一定要及时告知作者 前言 由于有些读者朋友私聊我&#xff0c;希望出几期基础算法的讲解&#xff0c;kmp&…

前缀和算法

文章目录 前言一、关于前缀和二、一维数组求前缀和1.求段区间前缀和2.例题&#xff1a;AcWing795. 前缀和AC代码 三、二维数组求前缀和1.求S[i,j]2.求&#xff08;x1&#xff0c;y1&#xff09;&#xff0c;&#xff08;x2&#xff0c;y2&#xff09;子矩阵的和3.例题&#xff…

前缀和详解

目录 前缀和概念前缀和代码前缀和例题题目介绍思路分析相关代码 总结 前缀和概念 前缀和&#xff1a;顾名思义&#xff0c;是要求前缀的总和&#xff0c;什么是前缀&#xff0c;对于一个存放数字的数组而言&#xff0c;前缀就是指的数组的前k项&#xff0c;因此对应的前缀和就…

前缀和与差分 图文并茂 超详细整理(全网最通俗易懂)

目录 1、前缀和2、前缀和算法有什么好处&#xff1f;3、二维前缀和4、差分5、一维差分6、二维差分 1、前缀和 前缀和是指某序列的前n项和&#xff0c;可以把它理解为数学上的数列的前n项和&#xff0c;而差分可以看成前缀和的逆运算。合理的使用前缀和与差分&#xff0c;可以将…

算法:解数独

题目内容 编写一个程序&#xff0c;通过已填充的空格来解决数独问题。 一个数独的解法遵循如下规则&#xff1a; 数字 1-9 在每一行只能出现一次。数字 1-9 在每一列只能出现一次。数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。 本题满足以下设定&#xff1a; …

算法_回溯_解数独

文章目录 解数独1.解法2.总结python算法 解数独 leetcode链接 1.解法 这道题是一个棋盘问题&#xff0c;而棋盘问题是一个典型的回溯问题。 首先思考普通人&#xff08;这里指没有受过专业训练的人&#xff09;解数独的方法&#xff0c;那就是首先在空白位置假设一个数字&a…

Java编程实战12:解数独

目录 解数独题目示例 1提示 解答解题思路完整代码 解数独 题目 编写一个程序&#xff0c;通过填充空格来解决数独问题。 数独的解法需 遵循如下规则&#xff1a; 数字 1-9 在每一行只能出现一次。数字 1-9 在每一列只能出现一次。数字 1-9 在每一个以粗实线分隔的 3x3 宫内…

使用java解数独

说明 先根据规则解数独, 规则1: 如果备选数字只有一个, 那么就填入这个数字规则2: 如果在3*3单元格中, 或者一行, 或者一列中, 某个备选数字在所有的备选数字中只出现了一次, 那么就填入这个数字. 再暴力破解数独, 依次填入备选数字, 如果不能解开, 换下一个备选数字, 直到数独…

力扣 37. 解数独

一、题目描述 编写一个程序&#xff0c;通过填充空格来解决数独问题。 数独的解法需遵循如下规则&#xff1a; 数字 1-9 在每一行只能出现一次。数字 1-9 在每一列只能出现一次。数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。数独部分空格内已填入了数字&#xf…

LeetCode算法 —— 解数独

题目如下所示&#xff1a; 编写一个程序&#xff0c;通过已填充的空格来解决数独问题。 一个数独的解法需遵循如下规则&#xff1a; 数字 1-9 在每一行只能出现一次。 数字 1-9 在每一列只能出现一次。 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。 空白格用 ‘.…

C语言——解数独程序[源码]

用C语言写的解数独的程序。在linux下测试成功运行。 效果如图&#xff1a; 这是带解的数独&#xff0c;需要填写的部分用数字0代替。 这是程序运行后的效果图。看看&#xff0c;数独已经搞定啦&#xff5e;&#xff5e;&#xff5e; 程序源码如下&#xff1a; #include <…

用 Python 解数独(Sudoku)

芬兰数学家因卡拉花费3个月时间设计出的世界上迄今难度最大的数独。数独是 9 横 9 竖共有 81 个格子&#xff0c;同时又分为 9 个九宫格。规则很简单&#xff1a;每个空格填入 1~9 任意一个数字&#xff0c;需要保证每个横排和竖排以及九宫格内无相同数字。 解数独是一个可有可…

解数独c++

解数独 编写一个程序&#xff0c;通过填充空格来解决数独问题。 数独的解法需 遵循如下规则&#xff1a; 数字 1-9 在每一行只能出现一次。 数字 1-9 在每一列只能出现一次。 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。&#xff08;请参考示例图&#xff09; 数…

MATLAB 解数独

数独是一种较为常见的游戏&#xff0c;一般有4乘4、6乘6、9乘9几种&#xff0c;就像下面这种&#xff0c;本文也是主要求解此类数独。 此外还有一些奇形怪状的&#xff0c;如下图两种&#xff0c;均是不规则的&#xff0c;在此并不会涉及&#xff0c;但会在以后发布代码以及过程…

Leetcode 解数独

解数独 题目描述&#xff1a; 编写一个程序&#xff0c;通过填充空格来解决数独问题。一个数独的解法需遵循如下规则&#xff1a;数字 1-9 在每一行只能出现一次。 数字 1-9 在每一列只能出现一次。 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。提示&#xff1a;…

再战leetcode (解数独)

37. 解数独 题目描述 回溯法 leetcode题解 代码 public class Test {public static void main(String[] args) {char[][] board {{5, 3, ., ., 7, ., ., ., .},{6, ., ., 1, 9, 5, ., ., .},{., 9, 8, ., ., ., ., 6, .},{8, ., ., ., 6, ., ., ., 3},{4, ., ., 8, ., 3, .…

回溯算法解数独问题(java版)

下面来详细讲一下如何用回溯算法来解数独问题。 下图是一个数独题&#xff0c;也是号称世界上最难的数独。当然了&#xff0c;对于计算机程序来说&#xff0c;只要算法是对的&#xff0c;难不难就不知道了&#xff0c;反正计算机又不累。回溯算法基本上就是穷举&#xff0c;解这…

014. 解数独

1.题目链接&#xff1a; 37. 解数独 2.解题思路&#xff1a; 2.1.题目要求&#xff1a; 暂时的理解就是&#xff0c;编写一个程序然后自动填完数独&#xff0c;填完返回&#xff08;不用求解各种不同的数独组合&#xff09; 填的时候&#xff0c;数字要满足的规则&#xff1…

回溯算法—解数独

什么是回溯法&#xff1f; 回溯法&#xff08;探索与回溯法&#xff09;是一种选优搜索法&#xff0c;又称为试探法&#xff0c;按选优条件向前搜索&#xff0c;以达到目标。但当探索到某一步时&#xff0c;发现原先选择并不优或达不到目标&#xff0c;就退回一步重新选择&…