洛谷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)的异或和。
数据范围(:nleq 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;
}