1. 程式人生 > >前綴和 差分數組

前綴和 差分數組

gis += 先生 包含 更新 如果 noi pro pair

前綴和

一維前綴和:

\(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;
}

前綴和 差分數組