1. 程式人生 > >高維前綴和

高維前綴和

scan 輸入 long i++ ott style 有一個 下標 scanf

我們經常要用到前綴和。

一維:

for(int i=1;i<=n;i++)
b[i]=b[i-1]+a[i];

二維:

for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
         b[i][j]=b[i-1][j]+b[i][j-1]-b[i-1][j-1]+a[i][j];

那如果是三維的呢?

for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
        for(int k=1;k<=p;k++)
              b[i][j][k]
=b[i-1][j][k]+b[i][j-1][k]+b[i][j][k-1] -b[i-1][j-1][k]-b[i-1][j][k-1]-b[i][j-1][j-1] +b[i-1][j-1][k-1]

其實就是一個容斥。

但是,隨著維度t變高,容斥的復雜度是2^t,總復雜度O(n^t*2^t不能承受。

我們還有一個方法:

一維:

for(int i=1;i<=n;i++) 
    a[i]+=a[i-1];

二維:

for(int i=1;i<=n;i++)
    
for(int j=1;j<=m;j++) a[i][j]+=a[i][j-1]; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) a[i][j]+=a[i-1][j];

這個意思就是,第一遍前綴和,每個位置a[i][j]是,i行前j個的和。

第二遍,就把前面所有行的和加過來了。

分兩遍達到目的。看似麻煩。

那三維呢?

for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
        for(int p=1;p<=k;p++)
        a[i][j][k]
+=a[i-1][j][k]; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) for(int p=1;p<=k;p++) a[i][j][k]+=a[i][j-1][k]; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) for(int p=1;p<=k;p++) a[i][j][k]+=a[i][j][k-1];

其實和二維的理解是一樣的。再來一遍,把第三維的和加過去。

但是,這個三維只要3次,也就是說,對於t維,其實只要O(n^t*t)復雜度就很低了。

其實我們實際解題中,經常用的是n=2的情況。

比如,

例題:

1.部分和(牛客網NOIP賽前集訓營-普及組(第四場))

輸入一個長度為n的數組a[i],下標從0開始(0到n-1)
保證n是2的整數次冪,
對於每個i (0 <= i < n)
求所有滿足((i & j) == j)的a[j]之和。 n<=2^20 即,求每個i的子集和。 如果n=2^6,如果把i的二進制的表示:10101看做一個5維坐標的話, 那麽,i的子集就是這個坐標的高維前綴和。 可以發現,每個維度的n都是2, 這就比較好處理了。 如果是一般的:w表示最高維度:
for(int i=0;i<w;i++){
    for(int j=0;j<(1<<w);j++){
        if(j&(1<<i)) f[j]+=f[j^(1<<w)]; 
    }
}

本題:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=(1<<21);
ll a[N];
int n;
int main(){
    scanf("%d",&n);int p=0;
    for(int i=0;i<n;i++) scanf("%lld",&a[i]);
    for(int i=1;i<n;i<<=1){p++;
        for(int j=0;j<n;j++){
            if((j&(1<<p-1))) a[j]+=a[(j^(1<<p-1))];
        }
    }for(int i=0;i<n;i++) printf("%lld\n",a[i]);return 0;
}

復雜度和高維前綴和一樣;O(2^t*t)

高維前綴和