洛谷5495:Dirichlet前缀和
题目描述:
给定一个长度为\(n\)的数列\(a_1,a_2,a_3,...,a_n\)。
现在要求出一个长度为\(n\)的序列\(b_1,b_2,b_3,...,b_n\),满足:
\[
b_k=\sum_{i|k}a_i
\]
输出所有\(b\)的异或和。
数据范围\(:n\leq 10^7\)。
思路:
倍数法时间复杂度大约在\(O(nlnn)\)。
for(uint i = 1; i <= n; i++) a[i] = getnext(); for(int i = 1; i <= n; i++) for(int j = 1; j <= n/i; j++) b[i*j] += a[i];
结果是10个测试点T了9个。
根据算术基本定理,一个数可以被唯一的分解成几个质数相乘。
一个数\(x\)如果是另一个数\(y\)的约数。
那么\(x\)分解之后的质数和指数都是\(y\)的”子集“。
对于一个数,用预处理好的质数去处理它的贡献。
时间复杂度\(:O(nloglogn)\)。
#include<bits/stdc++.h> #define uint unsigned int using namespace std; const int maxn = 2e7 + 10; uint seed; inline uint getnext() { seed ^= seed<<13; seed ^= seed>>17; seed ^= seed<<5; return seed; } int primes[maxn], cnt; bool vis[maxn]; void init(int n) { for(int i = 2; i <= n; i++) { if(!vis[i]) primes[++cnt] = i; for(int j = 1; primes[j] <= n/i; j++) { vis[i*primes[j]] = 1; if(i % primes[j] == 0) break; } } } uint a[maxn], b[maxn], ans; int main() { init(maxn-5); uint n; cin >> n >> seed; for(uint i = 1; i <= n; i++) { a[i] = getnext(); b[i] = a[i]; } for(int i = 1; i <= cnt; i++) for(int j = 1; primes[i] <= n/j; j++) b[j*primes[i]] += b[j]; for(int i = 1; i <= n; i++) ans ^= b[i]; cout << ans << endl; return 0; }
来源:https://www.cnblogs.com/zxytxdy/p/12271871.html