Description
求 S ( n ) = ∑ i = 1 n f ( i ) S(n)=\sum_{i=1}^n f(i) S(n)=∑i=1nf(i)
Solution
part1(核心公式推导):
对于任何函数
g
(
i
)
g(i)
g(i),都有:
∑
i
=
1
n
(
f
∗
g
)
(
i
)
\sum_{i=1}^n(f*g)(i)
∑i=1n(f∗g)(i)
=
∑
i
=
1
n
∑
d
∣
i
f
(
d
)
g
(
i
d
)
=\sum_{i=1}^n\sum_{d|i}f(d)g(\frac{i}{d})
=∑i=1n∑d∣if(d)g(di)
=
∑
i
=
1
n
g
(
i
)
∑
i
∗
j
≤
n
f
(
j
)
=\sum_{i=1}^ng(i)\sum_{i*j≤n}f(j)
=∑i=1ng(i)∑i∗j≤nf(j)
=
∑
i
=
1
n
g
(
i
)
S
(
⌊
n
i
⌋
)
=\sum_{i=1}^ng(i)S(⌊\frac{n}{i}⌋)
=∑i=1ng(i)S(⌊in⌋)
那么
g
(
1
)
S
(
n
)
=
∑
i
=
1
n
(
f
∗
g
)
(
i
)
−
∑
i
=
2
n
g
(
i
)
S
(
⌊
n
i
⌋
)
g(1)S(n)=\sum_{i=1}^n(f*g)(i)-\sum_{i=2}^ng(i)S(⌊\frac{n}{i}⌋)
g(1)S(n)=∑i=1n(f∗g)(i)−∑i=2ng(i)S(⌊in⌋)
杜教筛就要构造一个
g
(
i
)
g(i)
g(i),使得
∑
i
=
1
n
(
f
∗
g
)
(
i
)
\sum_{i=1}^n(f*g)(i)
∑i=1n(f∗g)(i)与
g
(
i
)
g(i)
g(i)能快速计算
part2(复杂度证明):
假设计算
S
(
n
)
S(n)
S(n)的复杂度为
T
(
n
)
T(n)
T(n)
那么
T
(
n
)
=
O
(
n
)
+
∑
i
=
1
n
T
(
i
)
+
T
(
n
i
)
T(n)=O(\sqrt n)+\sum_{i=1}^{\sqrt n}T(i)+T(\frac{n}{i})
T(n)=O(n)+∑i=1nT(i)+T(in)(
i
i
i从
1
1
1枚举到
n
n
n时所有不同的
T
(
⌊
n
i
⌋
)
T(⌊\frac{n}{i}⌋)
T(⌊in⌋)之和)
据
t
a
n
g
j
z
tangjz
tangjz大佬所说:这里只展开一层就可以了,更深层的复杂度是高阶小量
所以
T
(
n
)
=
∑
i
=
1
n
O
(
i
)
+
O
(
n
i
)
T(n)=\sum_{i=1}^{\sqrt n}O(\sqrt i)+O(\sqrt\frac{n}{i})
T(n)=∑i=1nO(i)+O(in)
=
∑
i
=
1
n
O
(
n
i
)
=\sum_{i=1}^{\sqrt n}O(\sqrt\frac{n}{i})
=∑i=1nO(in)
=
n
∫
1
n
x
−
1
2
d
x
=\sqrt n\int_1^{\sqrt n}x^{-\frac{1}{2}}dx
=n∫1nx−21dx
=
n
⋅
2
x
1
2
∣
1
n
=\sqrt n\cdot 2x^{\frac{1}{2}}|_1^{\sqrt n}
=n⋅2x21∣1n
=
O
(
n
3
4
)
=O(n^\frac{3}{4})
=O(n43)
如果预处理了前
k
(
k
≥
n
)
k(k≥\sqrt n)
k(k≥n)个数的前缀和,那么
T
(
n
)
=
O
(
k
)
+
∑
i
=
1
n
k
O
(
n
i
)
=
O
(
k
+
n
k
)
T(n)=O(k)+\sum_{i=1}^\frac{n}{k}O(\sqrt\frac{n}{i})=O(k+\frac{n}{\sqrt k})
T(n)=O(k)+∑i=1knO(in)=O(k+kn)
当
k
=
O
(
n
2
3
)
k=O(n^\frac{2}{3})
k=O(n32)时
T
(
n
)
=
O
(
n
2
3
)
T(n)=O(n^\frac{2}{3})
T(n)=O(n32)
part3(example):
就拿
φ
(
i
)
φ(i)
φ(i)举例吧,有一个公式
φ
∗
1
=
i
d
1
φ*1=id_1
φ∗1=id1
所以
1
(
1
)
S
(
n
)
=
∑
i
=
1
n
i
d
1
(
i
)
−
∑
i
=
2
n
1
(
i
)
S
(
⌊
n
i
⌋
)
1(1)S(n)=\sum_{i=1}^nid_1(i)-\sum_{i=2}^n1(i)S(⌊\frac{n}{i}⌋)
1(1)S(n)=∑i=1nid1(i)−∑i=2n1(i)S(⌊in⌋)
S
(
n
)
=
n
(
n
+
1
)
2
−
∑
i
=
2
n
S
(
⌊
n
i
⌋
)
S(n)=\frac{n(n+1)}{2}-\sum_{i=2}^nS(⌊\frac{n}{i}⌋)
S(n)=2n(n+1)−∑i=2nS(⌊in⌋)
part4(代码实现中的注意点):
1.
1.
1.因为数据不止
1
1
1组,所以如果预处理
k
k
k个数的话,复杂度为
O
(
k
+
T
n
k
)
O(k+T\frac{n}{\sqrt k})
O(k+Tkn)
根据基本不等式,
k
k
k取
(
T
n
)
2
3
(Tn)^\frac{2}{3}
(Tn)32最为合适
2.
2.
2.计算
n
(
n
+
1
)
2
\frac{n(n+1)}{2}
2n(n+1)时,因为有
n
+
1
n+1
n+1,所以可能会爆
i
n
t
int
int,数论分块中的
l
l
l和
r
r
r也是如此
3.
3.
3.关于记忆化,有两种方法。一种是开unordered_map,另外一种是假设现在正在计算
S
(
n
′
)
S(n')
S(n′),那么只要判断
p
[
⌊
n
n
′
⌋
]
p[⌊\frac{n}{n'}⌋]
p[⌊n′n⌋]即可,数组大小可开到
O
(
n
k
)
的
规
模
O(\frac{n}{k})的规模
O(kn)的规模
Code
#include<cstdio>
#include<cstring>
typedef unsigned long long ll;
typedef unsigned int ui;
const int N=5000000;
int T,i,pr[N/14],n,cnt,j,t,mu[N],m[1291],nn;
bool vis[N];
ll phi[N],p[1291];
ll calp(int n){
if (n<N) return phi[n];
if (p[nn/n]) return p[nn/n];
ll ans=(1ll*n+1)*n>>1;
for (ui l=2,r;l<=n;l=r+1) r=n/(n/l),ans-=calp(n/l)*(r-l+1);
return p[nn/n]=ans;
}
int calm(int n){
if (n<N) return mu[n];
if (m[nn/n]) return m[nn/n];
int ans=1;
for (ui l=2,r;l<=n;l=r+1) r=n/(n/l),ans-=calm(n/l)*(r-l+1);
return m[nn/n]=ans;
}
int main(){
scanf("%d",&T);
phi[1]=mu[1]=1;
for (i=2;i<N;i++){
if (!vis[i]) pr[cnt++]=i,phi[i]=i-1,mu[i]=-1;
for (j=0;j<cnt && (t=i*pr[j])<N;j++){
vis[t]=1;
if (!(i%pr[j])){
phi[t]=phi[i]*pr[j];
mu[t]=0;
break;
}
phi[t]=phi[i]*(pr[j]-1);
mu[t]=-mu[i];
}
}
for (i=2;i<N;i++) phi[i]+=phi[i-1],mu[i]+=mu[i-1];
for (;T--;) scanf("%d",&n),nn=n,memset(p,0,sizeof(p)),
memset(m,0,sizeof(m)),printf("%llu %d\n",calp(n),calm(n));
}