洛谷传送门
BZOJ传送门
题目描述
给定一个正整数 N ( N ≤ 2 31 − 1 ) N(N\le2^{31}-1) N(N≤231−1)
求
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=1∑nφ(i)ans2=i=1∑nμ(i)
输入输出格式
输入格式:
一共 T + 1 T+1 T+1行 第 1 1 1行为数据组数 T ( T ≤ 10 ) T(T\le 10) T(T≤10) 第 2 ∼ T + 1 2\sim T+1 2∼T+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=1∑nh(i)=i=1∑nd∣i∑g(i)f(id)=d=1∑ng(d)i=1∑⌊dn⌋f(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=1∑nh(i)−d=2∑ng(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)=1−d=2∑nS(⌊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=2∑nS(dn)
预处理出前 5 ∗ 1 0 6 5*10^6 5∗106项, 然后用 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));
}
}