淺談差分數組的原理及簡單應用


一、差分數組的定義及用途

1.定義:

對於已知有n個元素的離線數列d,我們可以建立記錄它每項與前一項差值的差分數組f:顯然,f[1]=d[1]-0=d[1];對於整數i∈[2,n],我們讓f[i]=d[i]-d[i-1]。

2.簡單性質:

(1)計算數列各項的值:觀察d[2]=f[1]+f[2]=d[1]+d[2]-d[1]=d[2]可知,數列第i項的值是可以用差分數組的前i項的和計算的,即d[i]=f[i]的前綴和。
(2)計算數列每一項的前綴和:第i項的前綴和即為數列前i項的和,那么推導可知

即可用差分數組求出數列前綴和;

3.用途:

(1)快速處理區間加減操作:

假如現在對數列中區間[L,R]上的數加上x,我們通過性質(1)知道,第一個受影響的差分數組中的元素為f[L],即令f[L]+=x,那么后面數列元素在計算過程中都會加上x;最后一個受影響的差分數組中的元素為f[R],所以令f[R+1]-=x,即可保證不會影響到R以后數列元素的計算。這樣我們不必對區間內每一個數進行處理,只需處理兩個差分后的數即可;

(2)詢問區間和問題:

由性質(2)我們可以計算出數列各項的前綴和數組sum各項的值;那么顯然,區間[L,R]的和即為ans=sum[R]-sum[L-1];

二、相關題目

1.[HDU1556]Color the ball

Description

-N個氣球排成一排,從左到右依次編號為1,2,3....N.每次給定2個整數a b(a <= b),lele便為騎上他的“小飛鴿"牌電動車從氣球a開始到氣球b依次給每個氣球塗一次顏色。但是N次以后lele已經忘記了第I個氣球已經塗過幾次顏色了,你能幫他算出每個氣球被塗過幾次顏色嗎?
-Input:每個測試實例第一行為一個整數N,(N <= 100000).接下來的N行,每行包括2個整數a b(1 <= a <= b <= N)。當N = 0,輸入結束。
-Output:每個測試實例輸出一行,包括N個整數,第I個數代表第I個氣球總共被塗色的次數。

Solution

1.記錄各次操作,對差分數組進行對應修改,改變量為1(用途1);
2.使用性質(1)計算各項的值即可;

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std; 
int d[100010],a[100010],l,r;
int main(){
	int n;
	while(scanf("%d",&n),n)
	{
		memset(d,0,sizeof(d));
		memset(a,0,sizeof(a));
		for(int i=1;i<=n;++i){
			scanf("%d%d",&l,&r);
			d[l]+=1;
			d[r+1]-=1;
		}
		for(int i=1;i<=n;++i) a[i]=a[i-1]+d[i];
		for(int i=1;i<n;++i) printf("%d ",a[i]);
		printf("%d\n",a[n]);
	}
	return 0;
}

2.[NKOJ3754]數列游戲

Description

-給定一個長度為N的序列,首先進行A次操作,每次操作在Li和Ri這個區間加上一個數Ci。
然后有B次詢問,每次詢問Li到Ri的區間和。
初始序列都為0。
-輸入格式:
第一行三個整數N A B。(1<=N<=1000000,1<=A<=N,A<=B<=N)
接下來A行,每行三個數Li Ri Ci。(1<=Li<=N,Li<=Ri<=N,|Ci|<=100000000000000)。
接下來B行,每行兩個數 Li Ri。范圍同上。
-輸出格式:
對於每次詢問,輸出一行一個整數。
因為最后的結果可能很大,請對結果mod 1000000007。

Solution

1.應用(1)處理區間加;
2.用性質(1)求出修改后數列,再求出相應數列和(應用2)或直接用性質(2)求解;
3.注意隨時取模;

#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cstdio>
const long long mod=1000000007; 
using namespace std;
long long d[100010],f[100010],sum[100010];
int main(){
	int n,a,b;
	scanf("%d%d%d",&n,&a,&b);
	memset(d,0,sizeof(d));
	memset(f,0,sizeof(f));
	memset(sum,0,sizeof(sum));
	for(int i=1;i<=a;++i){
		long long l,r,c;
		scanf("%ld%ld%ld",&l,&r,&c);
		f[l]=(f[l]+c)%mod;
		f[r+1]=(f[r+1]-c)%mod;
	}
	for(int i=1;i<=n;++i)  d[i]=(d[i-1]+f[i])%mod;
	for(int i=1;i<=n;i++) sum[i]=(sum[i-1]+d[i])%mod;
	for(int i=1;i<=b;++i){
		int l,r;
		scanf("%d%d",&l,&r);
        printf("%ld\n",(sum[r]-sum[l-1])%mod);
		//printf("%ld\n",temp>=0?temp:temp+mod);//防止結果為負; 
	}
	return 0;
} 

3.[NOIP2012提高&洛谷P1083]借教室

題解隨筆:http://www.cnblogs.com/COLIN-LIGHTNING/p/8467795.html

4.[洛谷P3948]數據結構

題解隨筆:http://www.cnblogs.com/COLIN-LIGHTNING/p/8482463.html


免責聲明!

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



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