一维前缀和:
\(sum[i]=sum[i-1]+a[i]\)
二维前缀和:
\(S[i,j]=S[i-1,j]+S[i,j-1]-S[i-1,j-1]+A[i,j]\)
前缀和也没什么好讲的理论知识,做题会用就行了!
P2280 [HNOI2003]激光炸弹
https://www.luogu.org/problemnew/show/P2280
蒟蒻太懒了,不想手打题面,就直接分析吧
很显然,这道题是二维前缀和的运用;
我们可以先递推求出二维前缀和S,然后枚举边长为R的正方形的下标(右下角的下标),然后就可以直接得到正方形内所有目标的价值之和,并更新最大值
#include<bits/stdc++.h>
using namespace std;
int n,r,s[5021][5021],ans,x,y,val;
int main(){
scanf("%d%d",&n,&r);
for(int i=1;i<=n;i++){
scanf("%d%d%d",&x,&y,&val);
s[x+1][y+1]=val;
}
for(int i=1;i<=5001;i++)
for(int j=1;j<=5001;j++)
s[i][j]+=s[i-1][j]+s[i][j-1]-s[i-1][j-1];//递推求出二维前缀和
for(int i=0;i<=5000-r;i++)
for(int j=0;j<=5000-r;j++)
ans=max(ans,s[i+r][j+r]-s[i+r][j]-s[i][j+r]+s[i][j]);
//枚举每个正方形,直接计算得到该正方形的价值
printf("%d\n",ans);
return 0;
}
P2879 [USACO07JAN]区间统计Tallest Cow
https://www.luogu.org/problemnew/show/P2879
这个题面我没找到中文的,我还是太蒻(lan)了
我们可以建立一个数组c,起初数组中全为零(即看做每头牛都一样高),当有一条关系表示a和b可以互相看见时,则把数组c中下标区间为[a+1,b-1]的数都减去1(注意到这里就可以用我们上述讲到的差分数组的优化方法),因为第P头牛是最高的,则c[P]最后一定为零,第i头牛的身高就等于c[i]+H;
#include<bits/stdc++.h>
#define ll long long
#define gt getchar()
using namespace std;
inline int read(){
int k=0;char ch=gt;
while(ch<‘-‘)ch=gt;
while(ch>‘-‘)k=k*10+ch-‘0‘,ch=gt;
return k;
}
int n,p,h,m;
map<pair<int,int>,bool> existed;
int c[10005],d[10005];
int main(){
n=read();p=read();h=read();m=read();
for(int i=1;i<=m;i++){
int a,b;
a=read();b=read();
if(a>b) swap(a,b);
//始终使第a头牛的身高比第b头牛高
if(existed[make_pair(a,b)]) continue;
//检查关系是否重复出现,判重
d[a+1]--;d[b]++;
//差分数组优化
existed[make_pair(a,b)]=true;
//标记
}
for(int i=1;i<=n;i++){
c[i]=c[i-1]+d[i];
//d数组是c数组的差分数组
printf("%d\n",h+c[i]);
}
return 0;
}
差分数组:记录当前位置的数与上一位置的数的差值.
如果我们在差分数组的\(b_x\)减去\(val\),在\(b_{y+1}\)位置处加上\(val\),就能达到整个区间修改的操作.
这里可能不太好理解,但其实这就是一个结论,我们来举个例子:
现在有一个序列A:1 4 2 3 5
则其差分数组B:(1) 3 -2 1 2
现在我们要使序列A区间\([2,4]\)上的每个值都加2(我习惯数组从下标1开始记录)
对于序列A我们有常规操作得到 1 6 4 5 5
对于修改后的序列A,其新的差分数组为 (1) 5 -2 1 0
如果我们直接按照上面的结论对差分数组进行更改,则修改后的差分数组为(1) 5 -2 1 0;
于是我们发现这两种方法得到的差分数组是一样的!!!
根据时间复杂度和修改的难易程度,我们当然会选择后者进行更改,因为它只需要修改差分数组的两个位置的值;
代码实现:
#include<bits/stdc++.h>
using namespace std;
int n,m,q,last,sum[10086],b[10086],s[10086];
//b[]数组是对于原序列的差分数组
//s[]数组是通过差分数组可以得到原序列
//sum[]数组是前缀和数组
int main(){
cin>>n;//n个数
for(int i=1,x;i<=n;i++){
cin>>x;//这里实际上不需要a数组,视题而异
b[i]=x-last;//得到差分数组
last=x;//别忘了变化last变量
}
cin>>m;//m次操作
for(int i=1,l,r,val;i<=m;i++){
cin>>l>>r>>val;//在[l,r] 加上val
b[l]+=val,b[r+1]-=val;
}
for(int i=1;i<=n;i++){
s[i]=s[i-1]+b[i];//这里是处理s数组
sum[i]=sum[i-1]+s[i];//处理sum数组.
}
cin>>q;//q个询问
for(int i=1,l,r;i<=q;i++){
cin>>l>>r; //询问[l,r]的区间和.
cout<<sum[r]-sum[l-1]<<endl; //输出
}
}
模板题: AT2442 フェーン現象 (Foehn Phenomena)
你知道N+1个地点的海拔Ai,编号为0…N,有风从0吹向N,想让你求出地点N的风的温度.
第一行输入包括四个被空格隔开的整数N,Q,S,T.这表示JOI先生在地点N有一所房子,有Q次地壳运动,海拔每上升1米的话,风的温度会降低S度,海拔每下降一米的话,风的温度会上升T度.
接下来的N+1行中第i行(1≤i≤N+1)包含一个整数Ai?1,表示地壳运动前地点i?1的海拔高度.接下来的Q行中第j行((1≤j≤Q)包括三个被空格隔开的整数Lj,Rj,Xj.这表示第j天地壳运动使地点Lj到地点Rj中这些地点的海拔变化了Xj;
#include<bits/stdc++.h>
#define N 200005
#define R register
using namespace std;
long long n,q,s,t;
long long A[N],last,ans;
inline void read(long long &x){
int f=1;x=0;char s=getchar();
while(!isdigit(s)){if(s==‘-‘)f=-1;s=getchar();}
while(isdigit(s)){x=x*10+s-‘0‘;s=getchar();}
x*=f;
}//快读优化
inline long long get(long long x){
return x > 0 ? -(x*s) : -(x*t) ;
}//定义函数get计算风的温度的变化
int main(){
read(n),read(q),read(s),read(t);
read(last);
for(R int i=1;i<=n;i++){
R long long x;
read(x);
A[i]=x-last;//差分数组
last=x;//不断更新last
ans+=get(A[i]);//ans初始化N点的温度
}
for(R long long x,y,z;q;q--){
read(x),read(y),read(z);
ans-=get(A[x]);
A[x]+=z;
ans+=get(A[x]);
if(y!=n){
//差分数组改变y+1的值,所以判断一下
ans-=get(A[y+1]);
A[y+1]-=z;
ans+=get(A[y+1]);
}
printf("%lld\n",ans);
}
return 0;
}
原文:https://www.cnblogs.com/PPXppx/p/9861383.html