高维前缀和详解

发布时间 2023-08-31 22:13:54作者: 铃狐sama

高维前缀和详解

背景:

sensei:我们随便上点技巧类型的东西吧,就这个高位前缀和......(讲了一堆k维前缀和复杂度证明后)......好我们看看版题。
版题:
现在有n(n≤20)个物品,确定每个物品的选取与否可以表示一个集合,那么这n个物品最多可以表示个2的n次方个集合。
现在对每个集合都给定个权值,然后查询某个集合以及其包含的所有子集的权值和。
sensei:很简单吧,直接用高维就好了。
我:???高维在哪里???

现实版的高维前缀和(真就k维这样)

引入:

我们拿k=2来进行引入:
一般的二维前缀和是利用的容斥原理,加加减减四项好好写哦。
k=3,没关系,稍微复杂点而已。
k=20,没关系,自毙了而已。
OI大神:无所谓我可以慢慢熬。
sosdp:您加油,我先A了。

写法:

那么高级的前缀和怎么写:对于每一维上轮流求前缀和。
k=2:

for(int i=1;i<=n;++i)//先求数组a关于第一个维度的前缀和
{
    for(int j=1;j<=m;++j)
    {
        a[i][j]=a[i][j]+a[i][j-1];
    }
} 
for(int i=1;i<=n;++i)//在已经求完一个维度前缀和的基础上求数组a关于第二个维度的前缀和
{
    for(int j=1;j<=m;++j)
    {
        a[i][j]=a[i][j]+a[i-1][j];
    }
}

k=3:

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];
        }
    }
}

这样写的意义:

假设我们求的是k维前缀和,对于原来的算法,我容斥原理要计算2的n次方次,已经不是常数了......
现在这个新的算法的时间就是k*容量了,大大减少时间复杂度和思考程度

抽象的高维前缀和

引入:

现实版高维前缀和倒下了。
某OIer:就这就这就这???
抽象版高维前缀和上场了。
OIer倒下了。
求解子集/超集:就这就这就这???

正文:

现在我们考虑抽象的高位前缀和:子集和超级。
我们先拿子集来说。
对于长度为2的情况,有四种情况:01/00/11/10。
放到二维平面上考虑
我们想求在这四种情况下的权值和,a00+a01+a10+a11,发现正是二维前缀和!
很显然这种情况可以扩展到k维,取到的前缀和就表示长度为k的子集的权值和。
然后我们会发现:a数组的k个维度都是0或者1,那我们可以用bitset或者一个二进制数直接表示整体(这样就不用开k维数组了)
代码如下:

for(int i=0;i<w;++i)//依次枚举每个维度
{
    for(int j=0;j<(1<<w);++j)//求每个维度的前缀和
    {
        if(j&(1<<i))s[j]+=s[j^(1<<i)]; 
    }
}