【luogu P5495】Dirichlet 前缀和(高维前缀和)
Dirichlet 前缀和
题目链接:luogu P5495
题目大意
给你一个数组,要你求它狄利克雷卷积数组的异或和。
思路
考虑那些位置会被贡献给 \(x\)。
先拆分:\(p_1^{k_1}p_2^{k_2}...p_{m}^{k_{m}}\)
然后就是所有的 \(p_1^{k_1'}p_2^{k_2'}...p_{m}^{k_{m}'}\) 满足所有 \(k'_i\leq k_i\)。
那我们考虑简单一点,只有一个质数,那就是 \(p^k\) 的值要是 \(p^{0}\sim p^{k}\) 的值加起来,那不就是前缀和嘛。
那这里是多个质数,就是每个质数是一维,弄个高维前缀和就可以啦。
那高维前缀和你就像二维前缀和一样(不要用容斥的那个),就先横着前缀一次,再纵着前缀一次。
那对应到高维前缀和就是每一维都前缀和一次就可以啦。
代码
#include<cstdio>
using namespace std;
#define uint unsigned int
uint seed;
inline uint getnext(){
seed^=seed<<13;
seed^=seed>>17;
seed^=seed<<5;
return seed;
}
const int N = 2e7 + 100;
int n;
bool np[N];
uint a[N], b[N], ans;
void work() {
np[1] = 1;
for (int i = 1; i <= n; i++) b[i] = a[i];
for (int i = 1; i <= n; i++) {
if (np[i]) continue;//枚举每个质数
for (int j = 1; i * j <= n; j++)//前缀和
b[i * j] += b[j], np[i * j] = 1;
}
}
int main() {
scanf("%d %u", &n, &seed);
for (int i = 1; i <= n; i++) a[i] = getnext();
work();
for (int i = 1; i <= n; i++) ans ^= b[i];
printf("%u", ans);
return 0;
}