[SOJ #112]Dirichlet 前缀和

题目大意:给定一个长度为$n$的序列$a_n$,需要求出一个序列$b_n$,满足:
$$
b_k=\sum\limits_{i|k}a_i
$$
$n\leqslant10^7$

题解:$\mathrm{Dirichlet}$前缀和,考虑把$k$写成一个无穷向量$[\beta_1,\beta_2,\beta_3,\cdots]$,满足$k=\sum\limits_iP_i^{\beta_i}$,$P_i$为第$i$个质数。相同的,把$i$写成$[\alpha_1,\alpha_2,\alpha_3,\cdots]$,于是:

$$
\begin{align*}
b_{\beta_{k,1},\beta_{k,2},\beta_{k,2},\cdots}&=\sum\limits_{i|k}a_{\alpha_{i,1},\alpha_{i,2},\alpha_{i,3},\cdots}\\
&=\sum\limits_{\forall\beta_{k,j}\geqslant\alpha_{i,j}}a_{\alpha_{i,1},\alpha_{i,2},\alpha_{i,3},\cdots}
\end{align*}
$$
于是先线性筛出质数,再做一个高维前缀和即可。复杂度$O(n\log\log n)$

卡点:

 

C++ Code:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
const int maxn = 1e7 + 5;
typedef unsigned int uint;
typedef unsigned long long ull;

struct random {
	ull seed;
	static const ull multiplier = 0x5deece66dll;
	static const ull addend = 0xbll;
	static const ull mask = 0xffffffffffffll;

	void set_seed (ull _seed) {seed = _seed;}

	uint next() {
		seed = (seed * multiplier + addend) & mask;
		return seed >> 16;
	}
} rnd;

int plist[maxn >> 3], ptot;
bool notp[maxn];
void sieve(const int n) {
	for (int i = 2; i <= n; ++i) {
		if (!notp[i]) plist[ptot++] = i;
		for (int j = 0, t; (t = i * plist[j]) <= n; ++j) {
			notp[t] = 1;
			if (i % plist[j] == 0) break;
		}
	}
}

int n;
uint s[maxn];
int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	std::cin >> n >> rnd.seed;
	for (int i = 1; i <= n; ++i) s[i] = rnd.next();
	sieve(n);
	for (int i = 0; i < ptot; ++i) {
		const int P = plist[i];
		for (int j = 1, t; (t = j * P) <= n; ++j)
			s[t] += s[j];
	}
	uint ans = 0;
	for (int i = 1; i <= n; ++i) ans ^= s[i];
	std::cout << ans << '\n';
	return 0;
}

  

转载于:https://www.cnblogs.com/Memory-of-winter/p/11323840.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值