[Luogu P4213] [BZOJ 3944] 【模板】杜教筛(Sum)

洛谷传送门
BZOJ传送门

题目描述

给定一个正整数 N ( N ≤ 2 31 − 1 ) N(N\le2^{31}-1) N(N2311)


a n s 1 = ∑ i = 1 n φ ( i ) a n s 2 = ∑ i = 1 n μ ( i ) ans_1=\sum_{i=1}^n\varphi(i) \\ ans_2=\sum_{i=1}^n \mu(i) ans1=i=1nφ(i)ans2=i=1nμ(i)

输入输出格式

输入格式:

一共 T + 1 T+1 T+1行 第 1 1 1行为数据组数 T ( T ≤ 10 ) T(T\le 10) T(T10) 2 ∼ T + 1 2\sim T+1 2T+1行每行一个非负整数 N N N,代表一组询问

输出格式:

一共 T T T行,每行两个用空格分隔的数 a n s 1 , a n s 2 ans_1,ans_2 ans1,ans2

输入输出样例

输入样例#1:
6
1
2
8
13
30
2333
输出样例#1:
1 1
2 0
22 -2
58 -3
278 -3
1655470 2

解题分析

还是简单说说杜教筛吧…

这玩意就是用来在 O ( n 3 4 ) O(n^{\frac{3}{4}}) O(n43) O ( n 2 3 ) O(n^{\frac{2}{3}}) O(n32)处理出一个积性函数的指定前缀和项, 常有毒瘤出题人用这玩意卡线筛。

具体而言, 假设我们的函数是 f ( x ) f(x) f(x), 要求的是 S ( n ) = ∑ i = 1 n f ( i ) S(n)=\sum_{i=1}^{n}f(i) S(n)=i=1nf(i), 那么我们搞出另一个积性函数 g ( x ) g(x) g(x), 得到 h ( x ) = g ( x ) ∗ f ( x ) h(x)=g(x)*f(x) h(x)=g(x)f(x),那么 :
∑ i = 1 n h ( i ) = ∑ i = 1 n ∑ d ∣ i g ( i ) f ( d i ) = ∑ d = 1 n g ( d ) ∑ i = 1 ⌊ n d ⌋ f ( i ) \sum_{i=1}^nh(i) \\ =\sum_{i=1}^n\sum_{d|i}g(i)f(\frac{d}{i}) \\ =\sum_{d=1}^ng(d)\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}f(i) i=1nh(i)=i=1ndig(i)f(id)=d=1ng(d)i=1dnf(i)
那么显然就有:
g ( 1 ) S ( n ) = ∑ i = 1 n h ( i ) − ∑ d = 2 n g ( d ) S ( ⌊ n d ⌋ ) g(1)S(n)=\sum_{i=1}^nh(i)-\sum_{d=2}^ng(d)S(\lfloor\frac{n}{d}\rfloor) g(1)S(n)=i=1nh(i)d=2ng(d)S(dn)
这个时候如果我们可以很快算出 ∑ i = 1 n h ( i ) \sum_{i=1}^{n}h(i) i=1nh(i) ∑ d = 2 n g ( d ) \sum_{d=2}^ng(d) d=2ng(d), 就可以对后面做一个下底分块, 然后递归处理了。

比如在这道题中, 对于筛 ∑ i = 1 n μ ( i ) \sum_{i=1}^{n}\mu(i) i=1nμ(i) ϵ = I ∗ μ ( i ) \epsilon=I*\mu(i) ϵ=Iμ(i), 那么:
S ( n ) = 1 − ∑ d = 2 n S ( ⌊ n d ⌋ ) S(n)=1-\sum_{d=2}^nS(\lfloor\frac{n}{d}\rfloor) S(n)=1d=2nS(dn)
对于筛 ∑ i = 1 n ϕ ( i ) \sum_{i=1}^{n}\phi(i) i=1nϕ(i) i d ( i ) = I ∗ ϕ ( i ) id(i)=I*\phi(i) id(i)=Iϕ(i), 那么:
S ( n ) = n ∗ ( n + 1 ) 2 − ∑ d = 2 n S ( n d ) S(n)=\frac{n*(n+1)}{2}-\sum_{d=2}^{n}S(\frac{n}{d}) S(n)=2n(n+1)d=2nS(dn)

预处理出前 5 ∗ 1 0 6 5*10^6 5106项, 然后用 m a p map map记忆化, 就可以在 O ( N 2 3 ) O(N^{\frac{2}{3}}) O(N32)的时间得到答案了。

注意下底分块的时候 l e f = r i g + 1 lef=rig+1 lef=rig+1会爆 i n t int int

代码如下:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <tr1/unordered_map>
#include <map>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define ll long long
#define MX 5000500
template <class T>
IN void in(T &x)
{
	x = 0; R char c = gc;
	for (; !isdigit(c); c = gc);
	for (;  isdigit(c); c = gc)
	x = (x << 1) + (x << 3) + c - 48;
}
int T, n, pcnt;
ll sum2[MX];
int phi[MX], miu[MX], pri[MX], sum1[MX];
bool npr[MX];
using std::map;
map <int, int> m1;
map <int, ll> m2;
void get()
{
	miu[1] = phi[1] = 1;
	R int i, j, tar;
	for (i = 2; i <= 5e6; ++i)
	{
		if (!npr[i]) pri[++pcnt] = i, miu[i] = -1, phi[i] = i - 1;
		for (j = 1; j <= pcnt; ++j)
		{
			tar = i * pri[j];
			if (tar > 5e6) break;
			npr[tar] = true;
			if (!(i % pri[j])) {miu[tar] = 0, phi[tar] = phi[i] * pri[j]; break;}
			miu[tar] = -miu[i], phi[tar] = phi[i] * phi[pri[j]];
		}
	}
	for (R int i = 1; i <= 5e6; ++i)
	sum1[i] = sum1[i - 1] + miu[i], sum2[i] = sum2[i - 1] + phi[i];
}
int Get_sum1(R unsigned int bd)//miu
{
	if (bd <= 5e6) return sum1[bd];
	if (m1.count(bd)) return m1[bd];
	ll ret = 1;
	for (R unsigned int lef = 2, rig; lef <= bd; lef = rig + 1)
	{
		rig = bd / (bd / lef);
		ret -= 1ll * (rig - lef + 1) * Get_sum1(bd / lef);
	}
	return m1[bd] = ret;
}
ll Get_sum2(R unsigned int bd)
{
	if (bd <= 5e6) return sum2[bd];
	if (m2.count(bd)) return m2[bd];
	ll ret = 1ll * bd * (bd + 1) / 2;
	for (R unsigned int lef = 2, rig; lef <= bd; lef = rig + 1)
	{
		rig = bd / (bd / lef);
		ret -= 1ll * (rig - lef + 1) * Get_sum2(bd / lef);
	}
	return m2[bd] = ret;
}
int main(void)
{
	get(); in(T);
	W (T--)
	{
		in(n);
		if (!n) puts("0 0");
		else printf("%lld %d\n", Get_sum2(n), Get_sum1(n));
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值