专栏文章
题解:P5495 【模板】Dirichlet 前缀和
P5495题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @min2l6iw
- 此快照首次捕获于
- 2025/12/01 19:32 3 个月前
- 此快照最后确认于
- 2025/12/01 19:32 3 个月前
题目分析
我们先学习一个 SOSDP。
问题:给定长度为 的数组 ,对于每个 ,计算:。
其中 表示 的二进制表示是 的二进制表示的子集。
做法:
表示:
只固定前 个比特位(比特位 到 ),让第 位及之后的比特位自由变化时,所有子集的和。
转移就很简单了。
对于第 个比特位( 从 到 ):
-
如果 的第 位是 : (这一位只能是 ,直接继承)
-
如果 的第 位是 : (这一位可以是 或 ,分别对应两种子集)
那我们回到这道题。
首先 等价于在质数指数空间中: 的每个维度指数 的对应维度指数。
那么,根据唯一分解定理,每个素数看成一维,然后就相当于是子集和,用高维前缀和即可。对每个素数 ,执行:
CPPfor(int j = 1; p * j <= n; j++)
a[p * j] += a[j];
高维前缀和就差不多是这么写。
CPPfor(int i = 0; i < n; i++) {
for(int S = 0; S < (1 << n); S++) {
if(S >> i & 1) { // 判第 i 位是不是 1。
f[S] += f[S ^ (1 << i)];
}
}
}
状态压缩,可以自己思考。
代码
CPP#include <bits/stdc++.h>
typedef unsigned int uint;
using namespace std;
uint seed;
inline uint getnext() {
seed ^= seed << 13;
seed ^= seed >> 17;
seed ^= seed << 5;
return seed;
}
int n;
uint a[20000005], ans;
bool f[20000005];
int main () {
cin >> n >> seed;
for(int i = 1; i <= n; i++) a[i] = getnext();
for(int i = 2; i <= n; i++) {
if(f[i]) continue;
for(int j = 1; i * j <= n; j++)
a[i * j] += a[j], f[j * i] = 1;
}
for (int i = 1; i <= n; i++) ans ^= a[i];
cout << ans;
return 0;
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...