原题链接
思路:
由唯一分解定理,可将两数 \(x,
y\) 分解为:
\[
x = \prod p_k^{\alpha_{x,k}}\\
y = \prod p_k^{\alpha_{y,k}}
\]
若 \(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 50
| #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]; } } for(int i(1);i<=n;++i) { ans ^= b[i]; } printf("%u",ans); return 0; }
|