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

前缀和与差分

作者:互联网

一、一维前缀和

场景模拟:

老师让班长糖豆帮着计算一下全班同学语文考试的总分,老师负责读每个同学的分数,糖豆负责计算。

老师:“第一名,张三 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]=s[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次,每次问从xy的位置,相加的和是几。

如果按普通想法,就是每问一次就计算一次,不利用以前的结果。这样假设每次的lr的距离是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