题目描述
传送门
题目大意:
ans1=∑ni=1φ(i)
,
ans2=∑ni=1μ(i)
n<=231−1
题解
对于
ans1=∑ni=1φ(i)
我们利用
n=∑d|nφ(d)
来进行化简,
φ(i)=n−∑d|i,d<iφ(d)
。 设
ϕ(n)=∑ni=1φ(i)
ϕ(n)=∑ni=1φ(i)=∑ni=1i−∑d|i,d<iφ(d)
=n∗(n+1)2−∑ni=2∑d|i,d<iφ(d)=n∗(n+1)2−∑ni=2∑⌊ni⌋d=1φ(d)
=n∗(n+1)2−∑ni=2ϕ(⌊ni⌋)
对于上面的式子我们只需要就算
n√
个
ϕ(⌊ni⌋)
就可以了。
至于时间复杂度我并不会算,不过网上有不少证明。
如果预处理
n23
的前缀和的话,时间复杂度可以达到
O(n23)
然后再看
ans2=∑ni=1μ(i)
利用
[n=1]=∑d|nμ(d)
进行化简,设
M(n)=∑ni=1μ(i)
1=∑ni=1[i=1]=∑ni=1∑d|iμ(d)
=∑ni=1∑⌊ni⌋d=1μ(d)
=∑ni=1M(⌊ni⌋)
所以 M(n)=1−∑ni=2M(⌊ni⌋)
关于杜教筛:
求
F(n)=∑f(i)
,存在
g=f∗I
,
I
表示恒等函数,即
我们定义
G(n)=∑g(i)
,就可以得到
F(n)=G(n)−∑F(n/i)
i从2开始
如果
G(n)
可以在一定时间内求解,那么我们最好情况下可以做到
O(n23)
的时间求解
F(n)
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<map>
#include<cstring>
#define N 5000000
#define LL long long
using namespace std;
int t;
int n,mu[N+30],prime[N+30],pd[N+30];
LL phi[N+30];
map<int,LL> ans,ans1;
void init()
{
mu[1]=1; phi[1]=1;
for (int i=2;i<=N;i++) {
if (!pd[i]) {
prime[++prime[0]]=i;
mu[i]=-1; phi[i]=i-1;
}
for (int j=1;j<=prime[0];j++) {
if (prime[j]*i>N) break;
pd[prime[j]*i]=1;
if (i%prime[j]==0) {
mu[i*prime[j]]=0;
phi[i*prime[j]]=phi[i]*prime[j];
break;
}
else mu[i*prime[j]]=-mu[i],phi[i*prime[j]]=phi[i]*(prime[j]-1);
}
}
for (int i=1;i<=N;i++) mu[i]+=mu[i-1];
for (int i=1;i<=N;i++) phi[i]+=phi[i-1];
}
LL sumphi(LL n)
{
if (n<=N) return phi[n];
if (ans[n]) return ans[n];
LL tmp; int j;
if (n&1) tmp=(n+1)/2LL*n;
else tmp=n/2LL*(n+1);
for (int i=2;i<=n;i=j+1){
if (n/i) j=min(n,n/(n/i));
else j=n;
tmp-=sumphi(n/i)*(LL)(j-i+1);
if (j==n) break;
}
ans[n]=tmp;
return tmp;
}
LL summu(LL n)
{
if (n<=N) return mu[n];
if (ans1[n]) return ans1[n];
LL tmp=1; int j;
for (int i=2;i<=n;i=j+1) {
if (n/i) j=min(n,n/(n/i));
else j=n;
tmp-=summu(n/i)*(LL)(j-i+1);
if (j==n) break;
}
ans1[n]=tmp;
return tmp;
}
int main()
{
freopen("a.in","r",stdin);
//freopen("my.out","w",stdout);
init();
scanf("%d",&t); ans.clear(); ans1.clear();
for (int i=1;i<=t;i++) {
scanf("%I64d",&n);
printf("%I64d %I64d\n",sumphi(n),summu(n));
}
}