前綴和與差分


導圖

導圖

前綴和

前綴和常用於快速地求解區間范圍內的元素總和。

一維前綴和

設元素存儲在a[N]中,我們設計一個數組s[N]s[i]對應第一個元素到第i個元素的總和,即\(s[i]=a[1]+a[2]+...+a[i]\)

一維前綴和的維護公式為:\(s[i]=s[i-1]+a[i]\)

若我們想快速求出區間\([L,R]\)范圍內的元素總和。

我們可以利用前綴和快速求解:\(sum_{[L,R]}=s[R]-s[L-1]\)

可通過圖片加深理解。

區間求和

二維前綴和

設元素存儲在a[N][N]中,我們設計一個數組s[N][N],用來存儲a[1][1]開始的矩陣總和。

s[i][j]的含義可看下圖。a[N][N]為無色部分,s[N][N]為深色部分。

那么如何維護二維的前綴和數組呢?可觀察下圖:

可發現s[i][j]的面積由橙色區域s[i-1][j]與藍色區域s[i][j-1]組成后,再去掉重疊部分紫色區域s[i-1][j-1]后加上本身位置的內容a[i][j]得到。

故得到公式:\(s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j]\)

若我們想快速的求出某個子矩陣的元素和,可進行如下處理。

我們設子矩陣左上位置為(xa,ya),右下位置為(xb,yb)。從而確定子矩陣的形狀。

觀察下圖,可以發現,子矩陣的總和可由紅色區域s[xb][yb]去掉藍色區域s[xb][ya-1]和橙色區域s[xa-1][yb]后,再加上重復減的紫色區域s[xa-1][ya-1]后得到。即,公式為:\(sum_{子矩陣}=s[xb][yb]-s[xb][ya-1]-s[xa-1][yb]+s[xa-1][ya-1]\)

差分

差分常用於對連續的某個區域快速進行增加和減少的值的操作。

一維差分

設元素存儲在a[N]中,我們設計一個差分數組b[N]b[i]對應a[i]a[i-1]的差值,即\(b[i]=a[i]-a[i-1]\)

若我們對差分數組b進行前綴和處理,可發現存在逆元特性,前綴和的內容等於原數組a的內容。

s[1]=b[1]=a[1]
s[2]=s[1]+b[2]=a[1]+a[2]-a[1]=a[2]
s[3]=s[2]+b[3]=a[2]+a[3]-a[2]=a[3]
    ...
s[i]=s[i-1]+b[i]=a[i-1]+a[i]-ba[i-1]=a[i]

若我們對b[i]的對加上x。

再進行前綴和處理。

可發現,相當於從i到最后的n,對所有的原數組內容加上了x。

故,若想對\([L,R]\)的范圍的值都加上x。可通過三步實現。

  1. b[L]+=x
  2. b[R+1]-=x
  3. 前綴和處理查分數組b

二維差分

設元素存儲在a[N][N]中,我們設計一個差分數組b[N][N],用來存儲a數組中相鄰元素的差值。

二維差分維護公式為:\(b[i][j]=a[i][j]-a[i][j-1]-(a[i-1][j]-a[i-1][j-1])=a[i][j]-a[i][j-1]-a[i-1][j]+a[i-1][j-1]\)

若我們對差分數組b進行前綴和處理,存在逆元特點,前綴和結果為原數組a中的內容。

若我們對差分數組b[xa][yb]+=x,再對差分數組求前綴和。可發現,(xa,ya)(n,n)的原數組內容都加上了x。

若我們想快速地對某個子矩陣區域的元素和加減值。

我們設子矩陣左上位置為(xa,ya),右下位置為(xb,yb)。從而確定子矩陣的形狀。

觀察下圖

可發現若想對子矩陣區域加上x,可先將紅色區域b[xa][ya]加上x,在將橙色區域b[xa][yb+1]與藍色區域b[xb+1][ya]減去x進行抵消,再將重復減去的紫色區域b[xb+1][yb+1]的內容加上來。

  1. b[xa][ya]+=x
  2. b[xa][yb+1]-=x
  3. b[xb+1][ya]-=x
  4. b[xb+1][yb+1]+=x

之后再對差分數組進行前綴和處理即可。

習題強化

P1115 最大子段和

#include <iostream>
#include <cstdio>
using namespace std;
int n;
int a[200005];
int dp[200005];
/*
a[i]
dp[i]=max(dp[i-1]+a[i],a[i])
 */
int maxs=-2e9;
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		dp[i]=max(dp[i-1]+a[i],a[i]);
		maxs=max(dp[i],maxs);
	}
	cout<<maxs;
	return 0;
}

P1719 最大加權矩形

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;

const int N=125;
int n;
int a[N][N],s[N][N];

int subSum(int xa,int ya,int xb,int yb){
	return s[xb][yb]-s[xa-1][yb]-s[xb][ya-1]+s[xa-1][ya-1];
}

int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			cin>>a[i][j];
			s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j];
		}
	}
	int ans=-80000;
	for(int xa=1;xa<=n;xa++){
		for(int ya=1;ya<=n;ya++){
			for(int xb=xa;xb<=n;xb++){
				for(int yb=ya;yb<=n;yb++){
					int sum=subSum(xa,ya,xb,yb);
					ans=max(ans,sum);
				}
			}
		}
	}
	cout<<ans;
	return 0;
}

P2367 語文成績

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int n,p;
int a[5000005];
int b[5000005];

int main(){
	int x,y,z;
	cin>>n>>p;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		b[i]=a[i]-a[i-1];
	}
	while(p--){
		cin>>x>>y>>z;
		b[x]+=z;
		b[y+1]-=z;
	}
	memset(a,0,sizeof(a));
	int mins=105;
	for(int i=1;i<=n;i++){
		a[i]=a[i-1]+b[i];
		mins=min(mins,a[i]);
	}
	cout<<mins;
	return 0;
}

P3397 地毯

#include <iostream>
using namespace std;
const int N=1e3+5;
int a[N][N];
int s[N][N];
int n,m;
int main(){
	int xa,ya,xb,yb;
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		cin>>xa>>ya>>xb>>yb;
		a[xa][ya]++;
		a[xa][yb+1]--;
		a[xb+1][ya]--;
		a[xb+1][yb+1]++;
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j];
			cout<<s[i][j]<<" ";
		}
		cout<<endl;
	}
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM