前缀和与差分
作者:互联网
一、一维前缀和
场景模拟:
老师让班长糖豆帮着计算一下全班同学语文考试的总分,老师负责读每个同学的分数,糖豆负责计算。
老师:“第一名,张三 100分”, 糖豆记录如下:100分
老师:“第二名,李四 99分”, 糖豆记录如下:199分
老师:“第三名,王五 98分”, 糖豆记录如下:297分
...
老师:“第四十五名,赵九 60分”, 糖豆记录如下:4239分
完事了,糖豆汇报总分:“4239分!任务结束!”
老师想了一想,问了一句:“那前十名共多少分?”
糖豆有点懵,只能让老师从第一名开始到第十名再读一次。_
老师又想问:"那前二十名共多少分?"
糖豆彻底懵了,只能让老师从第一名开始到第二十名再读一次。_
老师也疯了!!!
看来这个办法不太行,老师的需求总变化!
那有什么办法呢??聪明的糖豆总会有办法的:
记录前\(i\)个同学的分数总和!
来吧,老师,你说你想要啥?
我想要前20名的分数总和!
没问题,我记的就是这个,给你!
我想要20至30名的分数总和!
啊???还想这么要?怎么办呢?
我们用数学的公式来描述一下,这样方便说明:
\(a[i]\)代表\(i\)号同学分数,\(s[i]\)代表\(i\)号同学及他以前的所有同学的分数总和。
那么有下面的关系式:
\(s[i-1]=a[1]+a[2]+a[3]+...+a[i-1]\)
\(s[i]=\ \ \ \ \ \ \ a[1]+a[2]+a[3]+...+a[i-1]+a[i]\)
我们如果记录了这个\(s[i]\),就能回答好多的问题:
1、第5名同学的分数是多少?
答:\(s[5]-s[4]\)
2、第1名到第10名同学的分数和是多少?
答:\(s[10]\)
3、第5名到第10名同学的分数和是多少?
答:\(s[10]-s[4]\)
前缀和一般从数下标1开始,这是因为它的定义是\(s[i]=s[i-1]+a[i]\),如果\(i\)从\(0\)开始,\(s\)数组下标就会出现负数,这样还需一堆\(if\)判断,麻烦,所以,一般为了代码简单,我们都把\(a[0]=0\),通常在全局变量区域里定义\(a\)数组,这样连\(a[0]=0\)也省略了,\(s[0]\)其实也是定义在全局变量区域的,所以\(s[0]=0\),这样操作代码就简单了。
2、把前缀和整理一下公式:
\(s[n] \ \ \ \ \ \ \ =a[1]+a[2]+a[3]+...+a[n-1]+a[n]\)
\(s[n-1] =a[1]+a[2]+a[3]+...+a[n-1]\)
所以上面两个等式左右都相减,得到
\(s[n]-s[n-1]=a[n]\),移项就是 \(s[n]=s[n-1]+a[n]\)
3、一维前缀和使用的场景
给定一个原始数组,后面需要多次查询某一个的数值和,比如 1 2 3 4 6 3 7 8 9 10,10个数字,需要问N次,每次问从x
到y
的位置,相加的和是几。
如果按普通想法,就是每问一次就计算一次,不利用以前的结果。这样假设每次的l
到r
的距离是m
,很显然,共需要m*N
次操作,如果使用了前缀和的预处理,计算一次前缀和,就是N
次运算,得到一个结果数组S[N]
,以后每次查询都是 \(s[r]-s[l-1]\),就是一次运算,即O(1)的时间复杂度,快了很多。核心就是预处理,找规律嘛。
C++ 代码
#include<cstdio>
using namespace std;
const int N = 100010;
int q[N], s[N];
//一维前缀和
int main() {
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d", &q[i]);
for (int i = 1; i <= n; i++) s[i] = s[i - 1] + q[i];
while (m--) {
int l, r;
scanf("%d%d", &l, &r);
printf("%d\n", s[r] - s[l - 1]);
}
return 0;
}
标签:分数,10,前缀,糖豆,int,老师,差分 来源: https://www.cnblogs.com/littlehb/p/14957309.html