前綴和

前綴和

前綴和是一種很重要的思想 其實這個思想很簡單html

就是一次預處理 把原來O(N*M)的複雜度降到O(N+M)
c++

a[i]+...+a[j]=sum[j]-sum[i-1];git

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
inline ll read() {
    ll x=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)) (ch=='-')?(f=-1,ch=getchar()):ch=getchar();
    while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    return x*f;
}
int n;
const int N=1<<20;
int a[N],sum[N];
signed main(){
    memset(sum,0,sizeof(sum));//先清空數組
    n=read();
    for(register int i=1;i<=n;i++) a[i]=read();//讀入
    for(register int i=1;i<=n;i++) sum[i]=sum[i-1]+a[i];//前綴和
}

那麼區間的數值查詢該怎麼作呢數組

假設有m次查詢ide

 

for(register int i=1;i<=m;i++) {
        int x=read(),y=read();
        ans=sum[y]-sum[x-1];
    }

 

ans=∑a[x]+...a[y];
spa

如上所述code

a[x]+...a[y]=sum[y]-sum[x-1];htm

想一想看爲何要 -1 而不是 sum[y]-sum[x]blog

舉個例子:ip

n=5

i 1 2 3 4 5
a[i] 5 3 4 2 1
sum[i] 5 8 12 14 15

看例子 本身體會吧...(我太菜了講不明白

接下來來幾道例題

例1

 

Problem

 

給出一個含有n個整數的數列a,而且有m次詢問,每次詢問數列在區間[l,r][l,r]內的和,即求a[l]+a[l+1]++a[r]的值。

 

Input Data

 

第一行爲一個整數 T (1T50),表示共有T組輸入數據;
對於每組數據,第一行是兩個正整數 n,m (1n100000 ,1m1000)分別表明數列長度和詢問次數;
第二行有 n 個正整數,第 i 個數表示數列元素 a[i]1a[i]109​​)的值;
接下來 m 行,每行有兩個正整數 lr (lrn),表明詢問內容。

 

Output Data

 

每組數據輸出 m 行,每行一個數爲該次詢問的區間和。
保證數據都在64位正整數範圍內。

裸題

#include<bits/stdc++.h>
#define ULL unsigned long long
#define MAXN 100000+5
#define f(i,j,n) for(register int i=j;i<=n;i++)
using namespace std;
ULL T,a[MAXN],n,m;
void solve(int T) {
    while(T--) {
        memset(a,0,sizeof(a));
        cin>>n>>m;
        f(i,1,n) {
            int x;
            cin>>x;
            a[i]=a[i-1]+x;
        }
        f(i,1,m) {
            int l,r;
            cin>>l>>r;
            cout<<a[r]-a[l-1]<<endl;
        }
    }
}
int main() {

    cin>>T;
    solve(T);
    return 0;
}
例1

 

例2

Problem

給定n個整數構成的序列,全部相鄰的m個數有nm+1個,求其中的最小值。

Input Data

第一行,2個整數n和m,範圍在[3100000],n>m;
第二行,有n個正整數,範圍在[31000]。

Output Data

一個整數,表示最小值。

這題是經過預處理以後再進行枚舉(也是前綴和的一般作法)

 

#include<bits/stdc++.h>
#define ULL unsigned long long
#define MAXN 100000+5
#define f(i,j,n) for(register int i=j;i<=n;i++)
#define INF 2147483647
using namespace std;
ULL a[MAXN],n,m,MIN=INF;
void solve(int T) {
    while(T--) {
        memset(a,0,sizeof(a));
        cin>>n>>m;
        f(i,1,n) {
            int x;
            cin>>x;
            a[i]=a[i-1]+x;
        }
        f(i,m,n) MIN=min(MIN,a[i]-a[i-m]);
        cout<<MIN<<endl;
    }
}
int main() {
    solve(1);
    return 0;
}
例2

 

 

例3

在一個nm的只包含0和1的矩陣裏找出一個不包含0的最大正方形,輸出邊長。

Input Data

第一行爲兩個整數n,m1n,m100),接下來n行,每行m個數字,用空格隔開,0或1。

Output Data

一個整數,最大正方形的邊長

#include<bits/stdc++.h>
#define ULL unsigned long long
#define MAXN 100+5
#define f(i,j,n) for(register int i=j;i<=n;i++)
using namespace std;
ULL T,a[MAXN][MAXN],n,m;
int q(int i,int j,int k,int l) {
    f(p,i,j)
    f(q,k,l)
    if(a[p][q]==0) return 0;
    return min(j-i+1,l-k+1);
}
void solve(int T) {
    int ans=0;
    while(T--) {
        memset(a,0,sizeof(a));
        cin>>n>>m;
        f(i,1,n)
        f(j,1,m) {
            int x;
            cin>>x;
            a[i][j]=x;
        }
        f(i,1,n)
        f(j,i,n)
        f(k,1,m)
        f(l,k,m)
        ans=max(ans,q(i,j,k,l));
        cout<<ans<<endl;
    }
}
int main() {
    solve(1);
    return 0;
}
例3

例4

拓展(二維前綴和)

#include<bits/stdc++.h>
#define f(i,j,n) for(register int i=j;i<=n;i++)
using namespace std;
const int N=100;
int n,m;
int a[N][N];
int main() {
    scanf("%d",&n,&m);
    f(i,1,n)
    f(j,1,m) {
        int x;
        scanf("%d", &x);
        a[i][j]=x+a[i-1][j]+a[i][j-1]-a[i-1][j-1];
    }
    int x1, y1, x2, y2;
    while(~scanf("%d%d%d%d",&x1,&y1,&x2,&y2)) printf("%d\n",a[x2][y2]-a[x1-1][y2]-a[x2][y1-1]+a[x1-1][y1-1]);
    return 0;
}
二維前綴和

BZOJ1218
HNOI2003

Problem

一種新型的激光炸彈,能夠摧毀一個邊長爲R的正方形內的全部的目標。
如今地圖上有n(n10000)個目標,用整數Xi​​,Yi​​(其值在[0,5000][0,5000])表示目標在地圖上的位置,每一個目標都有一個價值。激光炸彈的投放是經過衛星定位的,但其有一個缺點,就是其爆破範圍,即那個邊長爲R的正方形的邊必須和xy軸平行。若目標位於爆破正方形的邊上,該目標將不會被摧毀。

Input Data

輸入文件的第一行爲正整數n和正整數R
接下來的n行每行有3個正整數,分別表示xi​​,yi​​,vi​​

Output Data

輸出文件僅有一個正整數,表示一顆炸彈最多能炸掉地圖上總價值爲多少的目標(結果不會超過32767)。

#include <bits/stdc++.h>
#define f(i,j,n) for(register int i=j;i<=n;i++)
#define M 5010
using namespace std;
int n,r,ans,sum[M][M];
int main() {
    int x,y,z;
    cin>>n>>r;
    f(i,1,n) scanf("%d%d%d",&x,&y,&z),sum[x+1][y+1]+=z;
    f(i,1,5000) f(j,1,5000) sum[i][j]+=sum[i-1][j];
    f(i,1,5000) f(j,1,5000) sum[i][j]+=sum[i][j-1];
    f(i,r,5000) f(j,r,5000) ans=max(ans,sum[i][j]+sum[i-r][j-r]-sum[i][j-r]-sum[i-r][j]);
    cout<<ans<<endl;
    return 0;
}
例4

(代碼都是很久之前寫的了 碼風可能不太好 見諒)

相關文章
相關標籤/搜索