[學習筆記]高維前綴和


我們經常要用到前綴和。

一維:

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]+a[i][j][k];

其實就是一個容斥。

但是,隨着維度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 k=1;k<=p;k++)
        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 k=1;k<=p;k++)
        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 k=1;k<=p;k++)
        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<<i)]; 
    }
}

理解一下,就是先處理前i-1位是子集的所有位置的和,對於第i位,前i位是子集的要么第i位是0,要么第i位是1,

第i位是0的話,權值就是f[j^(1<<i)],(后面的維度坐標一定要一致。)

 

本題:

#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)

 


 

upda:2019.3.18

可以代替FWT的and和or卷積,只要"IFMT"一下(就是反着做)即可

復雜度相同,

 


 

upda:2019.4.17

 

實際上

 

FMT很辣雞

 

相比之下,FWT做的事情完全包含FMT,並且常數是FMT的1/2!

 

[WC2018]州區划分(這個題我人傻常數大,必須用FWT卡常才能過)

 

所以還是寫FWT吧

 

[學習筆記]FWT——快速沃爾什變換


免責聲明!

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



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