zoukankan      html  css  js  c++  java
  • [学习笔记]高维前缀和

    我们经常要用到前缀和。

    一维:

    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
    ",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——快速沃尔什变换

  • 相关阅读:
    Lock
    Semaphore
    Exchanger
    CyclicBarrier
    [O]SQL SERVER下有序GUID和无序GUID作为主键&聚集索引的性能表现
    十个面向对象设计原则
    DB-Engines Ranking
    【转】计算机网络总结
    【转】常见的Web实时消息交互方式和SignalR
    【转】软件质量之道:SourceMonitor
  • 原文地址:https://www.cnblogs.com/Miracevin/p/9778266.html
Copyright © 2011-2022 走看看