Luogu 6097 【模板】子集卷积
upd 2023/3/16:更改了时间复杂度的错误。
其实是暴力。
因为这是模板题,所以模板的前置知识也要讲。
- 前置知识:FWT 计算或卷积。
这里只需要掌握快速计算或卷积的方法,所以内容较少。如果向了解更多(比如异或卷积)的话可以去 P4717 看看。
例题:给定长度为 的序列 ,求 序列中每一项的值。我们需要一个 的解法。
考虑根据 构造两个序列 ,若 均为 并且这个过程可逆,令 ,若 能还原回 ,那么我们就可以 计算 。
在这里,我们令 (也就是 为 的子集)。那么有 。
那么有:
也就是说我们证明了这个 是的确能映射到一个正确的 的。现在考虑如何快速求 。
显然可以从低到高枚举每个二进制位,然后当前位为 的是右边对应位置为 的子集,从左依次贡献到右即可。
相当于求一个逆过程,右边依次减去左边的贡献即可。
参考了这里的代码。
void fwt(int *s, int op) {
op = (op + mod) % mod;
for (int o = 2, k = 1; o <= S + 1; o <<= 1, k <<= 1)
for (int i = 0; i <= S; i += o)
for (int j = 0; j < k; j++)
(s[i + j + k] += 1ll * s[i + j] * op % mod) %= mod;
}
- 回到原题
你发现这东西就多加了一个限制 ,也就是说 无交。
考虑一个充要条件, 并且 其实就相当于 并且 , 表示 集合的大小,即 中 的个数。
所以可以预处理 表示满足 的 的值, 对 同理。
那么 。
令 , 表示进行或卷积,那么 。
FWT 预处理每个 和 的子集和,枚举这个 求出 的子集和,然后做逆的 FWT 就做完了。复杂度 。
const int maxs = (1 << 20) + 100;
const int mod = 1e9 + 9;
int n, S, a[maxs], b[maxs], c[21][maxs], f[21][maxs], g[21][maxs];
int p[maxs];
void fwt(int *s, int op) {
op = (op + mod) % mod;
for (int o = 2, k = 1; o <= S + 1; o <<= 1, k <<= 1)
for (int i = 0; i <= S; i += o)
for (int j = 0; j < k; j++)
(s[i + j + k] += 1ll * s[i + j] * op % mod) %= mod;
}
int main() {
n = read(), S = (1 << n) - 1;
for (int i = 1; i <= S; i++) p[i] = p[i >> 1] + (i & 1);
for (int i = 0; i <= S; i++) f[p[i]][i] = read();
for (int i = 0; i <= S; i++) g[p[i]][i] = read();
for (int i = 0; i <= n; i++) fwt(f[i], 1), fwt(g[i], 1);
for (int i = 0; i <= n; i++)
for (int j = 0; j <= i; j++)
for (int k = 0; k <= S; k++)
(c[i][k] += 1ll * f[j][k] * g[i - j][k] % mod) %= mod;
for (int i = 0; i <= n; i++) fwt(c[i], -1);
for (int i = 0; i <= S; i++) write(c[p[i]][i]), pc(' ');
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通