Sum
Description
Input
一共T+1行
第1行为数据组数T(T<=10)
第2~T+1行每行一个非负整数N,代表一组询问
Output
一共T行,每行两个用空格分隔的数ans1,ans2
Sample Input
6
1
2
8
13
30
2333
Sample Output
1 1
2 0
22 -2
58 -3
278 -3
1655470 2
很久以前,本蒟蒻很naive的认为
O(n)
O
(
n
)
的线性筛已经是最快的筛法了……
然而现在这个神奇的
O(n23)
O
(
n
2
3
)
的杜教筛完全推翻了本蒟蒻的想法…….
大千世界真是无奇不有…….
思路:
杜教筛入门题。
杜教筛可以用来解决一些积性函数的前缀和问题。
主要是利用了这类积性函数的一些神奇的性质来简化求值……
咱语文水平不高所以上面两句毫无意义的话可以无视。
不如我们来推一波?
看例子想必是最容易理解的了,尤其是同时看两个。
φ(n):
φ
(
n
)
:
对于
φ(n)
φ
(
n
)
,有一个性质:
∑d|nφ(d)=n
∑
d
|
n
φ
(
d
)
=
n
我们可以把它转化成这样:
φ(n)=n−∑d|n,d<nφ(d)
φ
(
n
)
=
n
−
∑
d
|
n
,
d
<
n
φ
(
d
)
定义它的前缀和为 ϕ(n) ϕ ( n ) ,有:
ϕ(n)=∑ni=1φ(i)
ϕ
(
n
)
=
∑
i
=
1
n
φ
(
i
)
=∑ni=1i−∑d|i,d<iφ(d)
=
∑
i
=
1
n
i
−
∑
d
|
i
,
d
<
i
φ
(
d
)
=n⋅(n+1)2−∑ni=2∑d|i,d<iφ(d)
=
n
⋅
(
n
+
1
)
2
−
∑
i
=
2
n
∑
d
|
i
,
d
<
i
φ
(
d
)
=n⋅(n+1)2−∑ni=2∑⌊ni⌋d=1φ(d)
=
n
⋅
(
n
+
1
)
2
−
∑
i
=
2
n
∑
d
=
1
⌊
n
i
⌋
φ
(
d
)
=n⋅(n+1)2−∑ni=2ϕ(⌊ni⌋)
=
n
⋅
(
n
+
1
)
2
−
∑
i
=
2
n
ϕ
(
⌊
n
i
⌋
)
然后因为 ⌊ni⌋ ⌊ n i ⌋ 的取值对于一段连续的 i i 是相同的,咱就可以把相同的用乘法加速计算,而不是一个个去枚举了。
对于
μ(n)
μ
(
n
)
,同样有一个性质:
[n=1]=∑d|nμ(d)
[
n
=
1
]
=
∑
d
|
n
μ
(
d
)
定义它的前缀和为 M(n) M ( n ) ,有:
1=∑ni=1[i=1]
1
=
∑
i
=
1
n
[
i
=
1
]
=∑ni=1∑d|iμ(d)
=
∑
i
=
1
n
∑
d
|
i
μ
(
d
)
=∑ni=1∑⌊ni⌋d=1μ(d)
=
∑
i
=
1
n
∑
d
=
1
⌊
n
i
⌋
μ
(
d
)
=∑ni=1M(⌊ni⌋)
=
∑
i
=
1
n
M
(
⌊
n
i
⌋
)
所以 M(n)=1−∑ni=2M(⌊ni⌋) M ( n ) = 1 − ∑ i = 2 n M ( ⌊ n i ⌋ ) ,同样可以用 ⌊ni⌋ ⌊ n i ⌋ 的相同取值来加速。
于是这题就可做了,用哈希或map来一波记忆化搜索,就可以做到上面的 O(n23) O ( n 2 3 ) 了~
实现上,如果你和咱一样懒并且不怕自己的程序的时间复杂度多一个 log l o g ,尽管和咱一样开map,时间还是可以接受的(反正只是一个不算太大的值)~
另外会爆int,直接开long long或者int计算时强转都是可以的~
#include<iostream>
#include<map>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef map<int,ll>::iterator m_it;
const int N=5000000;
int pri[N/5],tot;
ll phi[N],mu[N];
bool npri[N];
int n;
map<int,ll>phis,mus;
inline void init()
{
phi[1]=1;
mu[1]=1;
for(int i=2;i<N;i++)
{
if(!npri[i])
{
pri[++tot]=i;
mu[i]=-1;
phi[i]=i-1;
}
for(int j=1;j<=tot && pri[j]*i<N;j++)
{
npri[pri[j]*i]=1;
if(i%pri[j])
{
phi[pri[j]*i]=(pri[j]-1)*phi[i];
mu[pri[j]*i]=-mu[i];
}
else
{
phi[pri[j]*i]=phi[i]*pri[j];
mu[pri[j]*i]=0;
break;
}
}
}
for(int i=1;i<N;i++)
{
phi[i]+=phi[i-1];
mu[i]+=mu[i-1];
}
}
ll calc_phi(ll n)
{
if(n<N)
return phi[n];
m_it it;
if((it=phis.find(n))!=phis.end())
return it->second;
ll ret=n*(n+1)>>1,nxt;
for(ll i=2;i<=n;i=nxt+1)
{
nxt=n/(n/i);
ret-=(nxt-i+1)*calc_phi(n/i);
}
return phis[n]=ret;
}
ll calc_mu(ll n)
{
if(n<N)
return mu[n];
m_it it;
if((it=mus.find(n))!=mus.end())
return it->second;
ll ret=1,nxt;
for(ll i=2;i<=n;i=nxt+1)
{
nxt=n/(n/i);
ret-=(nxt-i+1)*calc_mu(n/i);
}
return mus[n]=ret;
}
int main()
{
int T;
scanf("%d",&T);
init();
while(T--)
{
scanf("%d",&n);
printf("%lld %lld\n",calc_phi(n),calc_mu(n));
}
return 0;
}