高维前缀和

高维前缀和、快速莫比乌斯变换

高维前缀和#

问题#

给定数组 fi,定义 gi=jifj,求 gi

jijori

解法#

常规做法是使用 O(3n) 的子集枚举的 trick。

用高维前缀和可以做到 O(n2n) 的优秀复杂度。

具体来说,按一定顺序枚举全集 U 中的每一个元素,逐个加入这些元素。

假设现在枚举到了 i,若 s 中含 i,则令 gs 加上 gs{i}

因为 gs{i} 包含了不含 i 的,且是 s 子集的集合;而 gs 则包含了含 i,且是 s 子集的集合。

参考代码#

copy 了别人写的代码

void SUM(int *a,int n){for(int i=0;i<n;i++)for(int j=0;j<(1<<n);j++)if(j&(1<<i))a[j]+=a[j^(1<<i)];}

高维差分#

只需将高维前缀和中的 + 变成 - 即可。想象前缀和与差分,不正是把 +- 了吗?

高维后缀和#

s 中含有 i 改成“不含”即可。

快速莫比乌斯变换#

问题#

给定 AB,定义 Ci=jork=iAj×Bk

解法#

AB 做高维前缀和,然后按位相乘,得到的结果恰是 C 高维前缀和的结果。再做一次高维差分就能得到真实的 C 数组了。

例题#

CF165E#

板题,只是因为这里应当有一份我的代码。

// g++ e.cpp -o e -std=c++14 -Wall -Wextra -Wshadow -O2

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5, V = 1 << 22;
int f[V], a[N];
int main()
{
    cin.tie(0)->sync_with_stdio(0);
    int n;
    cin >> n;
    for (int i = 1; i <= n; ++i)
    {
        cin >> a[i];
        f[a[i]] = a[i];
    }
    for (int i = 0; i < 22; ++i)
    {
        for (int j = 0; j < V; ++j)
        {
            if (j >> i & 1)
            {
                if (!f[j])
                    f[j] = f[j ^ (1 << i)];
            }
        }
    }
    for (int i = 1; i <= n; ++i)
    {
        if (!f[(V - 1) ^ a[i]])
        {
            cout << "-1 ";
        }
        else
        {
            cout << f[(V - 1) ^ a[i]] << " ";
        }
    }
}

另一道板题

Dirichlet 前缀和#

以每一个质数作为一维,则 Dirichlet 前缀和本质上就是高维前缀和。

但质数实际上非常多,每一个开一维不可接受,但有用状态数为 107 级别,可以不用压二进制,每个数的唯一分解形式即可以表示每一维。

for (int i = 1; i <= pril; ++i)
    for (int j = 1; j * pri[i] <= n; ++j)
        s[j * pri[i]] += s[j];

作者:Terminator-Line

出处:https://www.cnblogs.com/Terminator-Line/p/18722819

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   Terminator-Line  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示