一维前缀和
Max Subarray Sum
算法思路:对于固定的右边界
j
j
j,最大子数组和为:
p
[
j
]
−
min
i
<
j
p
[
i
]
p[j]-\min_{i<j} p[i]
p[j]−mini<jp[i],可以在遍历右边界的过程中去维护
min
i
≤
j
p
[
i
]
\min _{i \leq j} p[i]
mini≤jp[i],这样可以
O
(
1
)
O(1)
O(1) 地求出固定右边界
j
j
j 的最大子数组和。
模板代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e4+4;
int n,pfx[N],t;
signed main(){
cin>>n;
for(int i=1;i<=n;++i){
cin>>t;
pfx[i]=pfx[i-1]+t;
}
int max_subarray_sum=pfx[1],min_prefix_sum=pfx[0];
for(int i=1;i<=n;++i){
max_subarray_sum=max(max_subarray_sum,pfx[i]-min_prefix_sum);
min_prefix_sum=min(min_prefix_sum,pfx[i]);
}
cout<<max_subarray_sum<<'\n';
}
练习题:
Max Subarray Sum
题意:给定一个长为
n
n
n 的数列,求出最大的子数列和。要求输出 最大子数组和、左边界、有边界。(如果有多个子数组的和最大,输出字典序最小的左边界和有边界)
参考代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
int n,pfx[10004],t,T;
int left_id,right_id,max_subarray_sum,min_prefix_sum;
signed main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>T;
while(T--){
cin>>n;
for(int i=1;i<=n;++i){
cin>>t;
pfx[i]=pfx[i-1]+t;
}
left_id=1,right_id=1;
int tmp=1;
max_subarray_sum=pfx[1];
min_prefix_sum=pfx[0];
for(int i=1;i<=n;++i){
if(max_subarray_sum<pfx[i]-min_prefix_sum){
max_subarray_sum=pfx[i]-min_prefix_sum;
left_id=tmp;
right_id=i;
}
if(min_prefix_sum>pfx[i]){ //改变左边界
min_prefix_sum=pfx[i];
tmp=i+1;
}
}
cout<<max_subarray_sum<<' '<<left_id-1<<' '<<right_id-1<<'\n'; //数组的下标从0开始
}
}
二维前缀和
定义:sum[i][j]
:表示
(
1
,
1
,
i
,
j
)
(1,1,i,j)
(1,1,i,j)所围成的子矩形的前缀和。
预处理:
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j];
查询子矩阵 ( x 1 , y 1 , x 2 , y 2 ) (x_1,y_1,x_2,y_2) (x1,y1,x2,y2)的元素和:
int ask(int x1,int y1,int x2,int y2)
{
return sum[x2][y2]-sum[x2][y1-1]-sum[x1-1][y2]+sum[x1-1][y1-1];
}
练习题:
Codeforces Round #817 (Div. 4) E. Counting Rectangles
题意:给定
n
n
n个矩阵,长宽分别为
h
,
w
,
h,w,
h,w,。有
q
q
q次询问,每次询问给出
h
1
,
w
1
,
h
2
,
w
2
h_1,w_1,h_2,w_2
h1,w1,h2,w2,问
n
n
n个给定的矩阵中有多少个矩阵满足
h
s
<
h
i
<
h
b
and
w
s
<
w
i
<
w
b
h_{s}<h_{i}<h_{b} \text { and } w_{s}<w_{i}<w_{b}
hs<hi<hb and ws<wi<wb。
数据范围:
(
1
≤
h
i
,
w
i
≤
1000
)
\left(1 \leq h_{i}, w_{i} \leq 1000\right)
(1≤hi,wi≤1000),
(
1
≤
n
≤
1
0
5
;
1
≤
q
≤
1
0
5
)
\left(1 \leq n \leq 10^{5} ; 1 \leq q \leq 10^{5}\right)
(1≤n≤105;1≤q≤105)。
解题思路:2D前缀和
参考代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
int T,n,q,a[1003][1003],sum[1003][1003],x,y,h1,w1,h2,w2;
int ask(int x1,int y1,int x2,int y2)
{
return sum[x2][y2]-sum[x2][y1-1]-sum[x1-1][y2]+sum[x1-1][y1-1];
}
signed main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>T;
while(T--){
memset(sum,0,sizeof sum);
memset(a,0,sizeof a);
cin>>n>>q;
for(int i=1;i<=n;++i){
cin>>x>>y;
a[x][y]+=x*y;
}
for(int i=1;i<=1000;++i)
for(int j=1;j<=1000;++j)
sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j];
while(q--){
cin>>h1>>w1>>h2>>w2;
cout<<ask(h1+1,w1+1,h2-1,w2-1)<<'\n';
}
}
}