洛谷5495:Dirichlet前缀和
洛谷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;
}