洛谷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)的异或和。

数据范围(: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;
}

原文地址:https://www.cnblogs.com/zxytxdy/p/12271871.html