您的位置:首页 > 理论基础 > 数据结构算法

noip数据结构与算法 之 基础小算法 1 一维前缀和维护

2017-05-23 20:53 309 查看

noip数据结构与算法 之 基础小算法 1 一维前缀和维护

一维前缀和维护是一种基础的小算法,该算法用我们所熟知的数列求和方式优化我们的某些查询操作,是一种动态规划的思想。

这里为介绍一维前缀和维护问题,我们来思考这个最简单的问题:

问题描述:

已知n个数的数列a,有m次询问,每次询问给定l,r两个数,求 al到 ar 内所有数的和。注意l到r的区间包含 al 和 ar 两个数。

输入数据:

第一行n和m,接下来一行有n个数,表示数列a,接下来有m行,每行有两个数l,r,详细解释参考问题描述。

输出数据:

m行,每行一个求和解。

输入样例:

5 2

1 2 3 4 5

2 4

3 5

输出样例:

9

12

数据范围:

0<N,M≤100000

ai 是int范围内的任意整数

对于这个问题而言,我们一开始想到的思路是对于每一次查询操作都遍历一遍我们的数组进行求和,这样的操作每一次都会遍历数组,是O(nm)的做法,而对于题目的数据量,这样一定会超时,于是我们要思考有没有更加优化的解。

先考虑暴力的情况,我们对于每一次查询操作,都遍历一遍数组,我们会发现,当我计算了某一区间值的时候,我计算其他区间,会重复计算某些值,我是否可以用一种方式,把这种重复利用起来。

于是我们想到了初中学过的前n项和问题。当我知道前r项和与前l-1项和,我是否就能求出从l到r的区间和?思考一下这个过程。前r项和里面包含了前l-1项和,所以做一个减法就能求出l到r的区间和。那么问题就到了我们如何求前n项和,这个问题更加简单,我们只需要遍历一次数组就可以求到这个前n项和的数组了,假设我们原数组是a,前n项和的数组是s,那么我们只需要s1=a1之后维护动规方程si=si−1+ai(i>1)即可。

那么问题迎刃而解,我们分析一下新解法的复杂度,求出s数组需要遍历一遍原数组,所以是O(n)的复杂度,对于每一次查询操作我们只需要做一次减法,所以是O(1)的复杂度,一共有m次,所以是O(m)的查询,最后的结果就是O(n+m)的时间复杂度。

以上就是关于一维的前缀和维护问题和解法的介绍。

我的代码如下:

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <ctime>
#include <cmath>
#include <algorithm>
using namespace std;

const int MAXN=1000010;

int n,m,a[MAXN];

//读入优化
int read(){
char ch=getchar();
bool fl=0;
int r=0;
if(ch=='-'){
fl=1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
r*=10;
r+=ch-'0';
ch=getchar();
}
return fl?-r:r;
}

//输出优化
void write(int x){
if(x<0){
putchar('-');
x=-x;
}
if(x>9){
write(x/10);
}
putchar(x%10+'0');
}

int main(){
freopen("in.txt","r",stdin);
freopen("std.txt","w",stdout);
n=read();
m=read();
a[0]=read();
for(int i=1;i<n;++i){
a[i]=read();
a[i]+=a[i-1];//前n项和求法,O(n)复杂度
}
for(int i=0;i<m;++i){
int l,r;
l=read();
r=read();
if(l==1){
write(a[r-1]);//输出结果,单次O(1),总共O(m)
}else{
write(a[r-1]-a[l-2]);//隐含的数组下标越界问题,在我写二维前缀和的时候发现的
}
putchar('\n');
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息