高维前缀和

我们经常要用到前缀和。

一维:

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)

原文地址:https://www.cnblogs.com/Miracevin/p/9778266.html

时间: 2024-08-01 15:06:06

高维前缀和的相关文章

4.7-4.9补题+水题+高维前缀和

题目链接:51nod 1718 Cos的多项式  [数学] 题解: 2cosx=2cosx 2cos2x=(2cosx)^2-2 2cos3x=(2cosx)^3-3*(2cosx) 数归证明2cos(nx)能表示成关于2cosx的多项式,设为f(n) f(1)=x,f(2)=x^2-2(其中的x就是2cosx) 假设n=1~k时均成立(k>=3) 当n=k+1时 由cos((k+1)x)=cos(kx)cos(x)-sin(kx)sin(x) cos((k-1)x)=cos(kx)cos(x)

hihocoder1496(高维前缀和)

题意:给定N个数A1, A2, A3, ... AN,小Ho想从中找到两个数Ai和Aj(i ≠ j)使得乘积Ai × Aj × (Ai AND Aj)最大.其中AND是按位与操作. 第一行一个整数N(1<=N<=100,000) 第二行N个整数A1, A2, A3, ... AN (0 <= Ai <2^20) 分析: 尝试枚举and值z,那么问题就变成了找寻最大的x*y,使得x&y==z 把这个要求放宽一点,我们来寻找z是x&y子集的情况(这样肯定不会丢掉整体最优

Codeforces 449D:Jzzhu and Numbers(高维前缀和)

[题目链接] http://codeforces.com/problemset/problem/449/D [题目大意] 给出一些数字,问其选出一些数字作or为0的方案数有多少 [题解] 题目等价于给出一些集合,问其交集为空集的方案数, 我们先求交集为S的方案数,记为dp[S],发现处理起来还是比较麻烦, 我们放缩一下条件,求出交集包含S的方案数,记为dp[S], 我们发现dp[S],是以其为子集的方案的高维前缀和, 我们逆序求高维前缀和即可,之后考虑容斥,求出交集为0的情况, 我们发现这个容斥

SPOJ Time Limit Exceeded(高维前缀和)

[题目链接] http://www.spoj.com/problems/TLE/en/ [题目大意] 给出n个数字c,求非负整数序列a,满足a<2^m 并且有a[i]&a[i+1]=0,对于每个a[i],要保证a[i]不是c[i]的倍数, 求这样的a[i]序列的个数 [题解] 我们用dp[i]表示以i为结尾的方案数, 我们发现要满足a[i]&a[i+1]=0,则dp[i]是从上一次结果中所有满足i&j=0的地方转移过来的 i&j=0即i&(~j)=i,即i为~

codeforces 938F(dp+高维前缀和)

题意: 给一个长度为n的字符串,定义$k=\floor{log_2 n}$ 一共k轮操作,第i次操作要删除当前字符串恰好长度为$2^{i-1}$的子串 问最后剩余的字符串字典序最小是多少? 分析: 首先很容易得到一个性质,那就是删除的那些串是可以不交叉的 很容易想到一个很简单的dp dp[i][j]表示考虑原串的前i位,删除状态为j的情况下字典序最小的字符串(注意dp里面保存的是个字符串) 那么很明显就是个O(n^3logn)的dp,无法通过 dp里是一个字符串这个东西是很浪费时间而且很不优美的

[2018.6.21集训]走路-分块高维前缀和-Pollard-Rho

题目大意 给一棵树,每个节点有一个权值$val$. 如果两个点$a$和$b$满足$a$为$b$的祖先且$val[b]$为$val[a]$的约数,那么可以从$a$一步跳到$b$. 求从$1$号节点走到各每个节点的路径数. $n \leq 10^5 , val[i] \leq 10^{18},$保证对于任意节点$i$,$val[i]$为$val[1]$的约数. 题解 首先有定理:一个数$n$的约数个数大概是$n^{\frac{1}{3}}$的级别 然后得到有理有据的一个部分分($val[i] \le

bzoj 5092 分割序列 —— 高维前缀和

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=5092 首先,处理出异或前缀和 s[i],i 位置的答案就是 s[j] + s[j]^s[i],j <= i 异或的套路是按位考虑,但是这里有加法...怎么考虑进位? 所以就不能考虑答案的这一位是什么,而应该考虑在这一位上的贡献,那么即使进位了,还是各位算各位的贡献,互相独立: 然后发现,如果 s[i] 在第 k 位上是1,那么 s[j] 的第 k 位无论是0还是1,总体的贡献都是 1<&

bzoj 5092 [Lydsy1711月赛]分割序列——高维前缀和

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=5092 套路地弄一个前缀异或和,就变成 f[ i ]=max_{j=0}^{i} { s[ j ] + (s[ i ]^s[ j ]) }.再套路地考虑按位贪心. 然后看了题解.按位贪心不是确定 f[ i ] 的这一位是0还是1,而是确定这一位是否给答案贡献 bin[ j ] ! 按位考虑,自己这一位如果是1,则 j 不管取在哪,都只有一种情况,就是向答案贡献 bin[ j ]: 自己这一位

CF449D Jzzhu and Numbers 高维前缀和

title CF449D Description 给出一个长度为 \(n\) 的序列 \(a_1,a_2...a_n\) . 求构造出一个序列 \(i_1 \le i_2 \le ... \le i_k\) 使得 \(a_{i_1}\&a_{i_2}\&...\&a_{i_k}=0\) .求方案数模 \(10^9+7\) . 也就是从 \(\{a_i\}\) 里面选出一个非空子集使这些数按位与起来为 \(0\) . analysis 这不是普转提的第四题吗?不得不防啊. 这也是一个