前綴和 差分數組
前綴和
一維前綴和:
\(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;
}
前綴和 差分數組