前缀和
假如给出一串长度为n的数列a1,a2,a3...an,再给出m个询问,每次询问给出L,R两个数,要求给出数列在区间[L,R]的和
普通的方法,时间复杂度为O(n*m)
int a[100005];
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
int sum=0;
while(m--){
for(int i=L;i<=R;i++){
sum+=a[i];
}
}
前缀和就是前面i个数的总和,对于每个询问,只需要输出a[R]-a[L-1]即可
用前缀和的方法,时间复杂度O(m+n)
前缀和求法
int a[0]=0;
for(int i=1;i<=n;i++){
a[i]+=a[i-1];
}
差分
假如给出一串长度为n的数列a1,a2,a3...an,再给出m次以及不同的L和R[L,R]操作
操作一:将[L,R]内的元素都加上P
操作二:将[L,R]内的元素都减去P
最后求[L,R]内的元素之和
普通方法时间复杂度为O(m*n)
利用差分+前缀和
#include <bits/stdc++.h>
using namespace std;
const int manx=1e5+5;
int a[maxn],b[maxn];
int main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
for(int i=1;i<=m;i++){
int l,r,t,p;//t为操作类型,p为加减的大小
cin>>t>>l>>r>>p;
if(t==1){//t==1表示加
b[l]+=p;b[r+1]-=p;//因为操作一我只需对[L,R]区间里的数加p,[R+1,n]这个区间里的数没必要加p,所以需要减掉p。
}else{//t==0表示减
b[l]-=p;b[r+1]+=p;
}
}
int add=0;
for(int i=1;i<=n;i++){
add+=b[i];
a[i]+=a[i-1]+add;
}
cin>>l>>r;
printf("%d
",a[r]-a[l-1]);
return 0;
}
裸差分
第一行输入两个整数n,q。(1<= n, q <= 1e5)接下来q行,
每行输入两个整数l, r(l <= r),表示andy让标号落在区间[l, r]里的数高度都加1
输出所有数的现状
#include <iostream>
#include <cstdio>
using namespace std;
const int maxn=1e5+10;
int a[maxn],b[maxn];
int main(){
int n,q;
cin>>n>>q;
while(q--){
int l,r;
scanf("%d%d",&l,&r);
b[l]++;
b[r+1]--;
}
for(int i=1;i<=n;i++){
a[i]+=a[i-1]+b[i];
}
for(int i=1;i<=n;i++){
printf("%d ",a[i]);
}
putchar('
');
return 0;
}
二维前缀和
给定一个n*m大小的矩阵a,有q次询问,每次询问给定x1,y1,x2,y2四个数,求以(x1,y1)为左上角坐标和(x2,y2)为右下角坐标的子矩阵的所有元素和。注意仍然包含左上角和右下角的元素。
二维前缀和求法
n行m列
memset(a,0,sizeof(a));
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
a[i][j]+=a[i][j-1]+a[i-1][j]-a[i-1][j-1];//因为a[i-1][j-1]的值被加了两次
}
}
1 2 3
2 1 2
2 1 1
前缀和
1 3 6
3 6 11
5 9 15