传送门
杜教筛好神奇啊
杜教太强辣
就是基于一些式子(懒得写了。)
然后预处理前
n2/3
的前缀和,然后复杂度就是
O(n2/3)
了
代码:
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<map>
#define ll long long
#define max(a,b) a>b?a:b
#define min(a,b) a<b?a:b
using namespace std;
const int N=5e6;
int prime[N+5],vis[N+5],cnt;
ll phi[N+5],mu[N+5];
map<int,ll> sphi,smu;
inline void init(){
phi[0]=mu[0]=0;
phi[1]=mu[1]=1;
for(int i=2;i<=N;i++){
if(!vis[i]){
prime[++cnt]=i;
phi[i]=i-1;
mu[i]=-1;
}
for(int j=1;j<=cnt&&i*prime[j]<=N;j++){
vis[i*prime[j]]=1;
if(i%prime[j]){
phi[i*prime[j]]=phi[i]*(prime[j]-1);
mu[i*prime[j]]=-mu[i];
}
else{
phi[i*prime[j]]=phi[i]*prime[j];
mu[i*prime[j]]=0;
break;
}
}
}
for(int i=2;i<=N;i++)
phi[i]+=phi[i-1],
mu[i]+=mu[i-1];
}
inline ll calc_phi(ll n){
if(n<=N)return phi[n];
map<int,ll>::iterator it;
if((it=sphi.find(n))!=sphi.end())return it->second;
ll i,last,ans=(n*(n+1))>>1;
for(i=2;i<=n;i=last+1){
last=n/(n/i);
ans-=(last-i+1)*calc_phi(n/i);
}
return sphi[n]=ans;
}
inline ll calc_mu(ll n){
if(n<=N)return mu[n];
map<int,ll>::iterator it;
if((it=smu.find(n))!=smu.end())return it->second;
ll i,last,ans=1;
for(i=2;i<=n;i=last+1){
last=n/(n/i);
ans-=(last-i+1)*calc_mu(n/i);
}
return smu[n]=ans;
}
int main(){
init();
ll T,n;
scanf("%lld",&T);
while(T--){
scanf("%lld",&n);
printf("%lld %lld\n",calc_phi(n),calc_mu(n));
}
return 0;
}