题解:P5495 【模板】Dirichlet 前缀和
题目分析
我们先学习一个 SOSDP。
问题:给定长度为
其中
做法:
(这一位只能是
- 如果
mask 的第i 位是1 :dp_{i +1,mask}=dp_{i,mask}+dp_{i,mask⊕(1<<i)} (这一位可以是
1 或0 ,分别对应两种子集)
那我们回到这道题。
首先
那么,根据唯一分解定理,每个素数看成一维,然后就相当于是子集和,用高维前缀和即可。对每个素数
for(int j = 1; p * j <= n; j++)
a[p * j] += a[j];
高维前缀和就差不多是这么写。
for(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)];
}
}
}
状态压缩,可以自己思考。
代码
#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;
}