一、差分數組的定義及用途
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;
}