(luogu5495) Dirichlet 前缀和

原题链接

思路:

由唯一分解定理, 可将两数$x, y$分解为:

若$a_x$对$b_y$有贡献, 则说明$\forall k, \alpha_{y,k} \ge \alpha_{x,k}$

相当于$b$是$a$的一个以质因子指数为维度的高维前缀和.

关于如何求高维前缀和, 可以用容斥, 但是这里使用了另一个方法: 求出前$n$维的前缀和后延新的维度推进求出$n+1$维前缀和.

以二维前缀和理解的话, 相当于先求出当前行的一维前缀和, 再延列推进求出二维前缀和.

时间复杂度与埃拉托色尼筛相当, 为$O(n \log \log n)$

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <cstdio>
#include <vector>
using std::vector;
typedef unsigned int uint;
const int Mn(20000005);
uint seed;
inline uint getnext(){
seed^=seed<<13;
seed^=seed>>17;
seed^=seed<<5;
return seed;
}

uint b[Mn];
bool isv[Mn];
vector<int> prm;

int main() {
int n;
scanf("%d %u",&n,&seed);
for(int i(2);i<=n;++i) { //欧拉筛求质数
if(!isv[i]) {
prm.push_back(i);
}
for(int a: prm) {
if(a*i>n) {
break;
}
isv[i*a] = true;
if(i%a==0) {
break;
}
}
}
for(int i(1);i<=n;++i) {
b[i] = getnext();
}
uint ans(0);
for(int a: prm) { //相当于维度
for(int j(1);j*a<=n;++j) { //延当前维度推进
b[a*j] += b[j]; //直接无符号溢出即对2^32取模
}
}
for(int i(1);i<=n;++i) {
ans ^= b[i];
}
printf("%u",ans);
return 0;
}