看完题一副不可做的样子 默默点开了题解 发现是杜教筛 就花了半天学习了一下
先说下杜教筛 可以在优于线性的复杂度内求出积性函数的前缀和
下求
∑ni=1f(i)
令
F(i)=∑ni=1f(i)
我们可以再引入一个积性函数
g(i)
则
∑ni=1f(i)∗g(i)=∑ni=1∑d|if(d)∗g(id)
=∑ni=1∑⌊ni⌋j=1f(j)∗g(i)=∑ni=1g(i)∑⌊ni⌋j=1f(j)=∑ni=1g(i)F⌊ni⌋
其中 ∑ni=1∑d|if(d)∗g(id)=∑ni=1∑⌊ni⌋j=1f(j)∗g(i) 可以理解为考虑每一个 f(i) 的贡献就是所有满足 i∗j≤n 的 f(j)∗g(i)
由之前推出的式子 我们可以得到
∑ni=1f(i)∗g(i)=∑ni=1g(i)F⌊ni⌋
将右边的
i=1
项提出来可以得到
g(1)∗F(n)=∑ni=1f(i)∗g(i)−∑ni=2g(i)F⌊ni⌋
一般我们把恒等函数
I
即
这样就有
F(n)=∑ni=1∑d|if(d)−∑ni=2F⌊ni⌋
所以题中的式子可以化为
∑ni=1ϕ(i)=n∗(n+1)2−∑ni=2∑⌊ni⌋j=1ϕ(j)
∑ni=1μ(i)=1−∑ni=2∑⌊ni⌋j=1μ(j)
然后复杂度是
O(n34)
(虽然我不会证) 如果预处理出
n23
以内的
F[i]
复杂度就是
O(n23)
(虽然我还是不会证)
然后就 可做 啦
#include<bits/stdc++.h>
#define bug(x) cout<<(#x)<<" "<<(x)<<endl
#define ll long long
using namespace std;
const int N=2e6+5;
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int T,n,m,cnt,prime[N];
ll phi[N],mu[N],p[N],q[N];
bool vis[N];
void pre(){
for(int i=2;i<=m;i++){
if(!phi[i]){
phi[i]=i-1,mu[i]=-1,prime[++cnt]=i;
}
for(int j=1;j<=cnt&&i*prime[j]<=m;j++){
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<=m;i++)phi[i]+=phi[i-1],mu[i]+=mu[i-1];
}
ll get_p(ll x){
return (x<=m)?phi[x]:p[n/x];
}
ll get_q(ll x){
return (x<=m)?mu[x]:q[n/x];
}
void solve(ll x){
if(x<=m) return;
int i,j=1,t=n/x;
if(vis[t]) return;vis[t]=1;
p[t]=x*(x+1)>>1;q[t]=1;
while(j<x){
i=j+1,j=x/(x/i);
solve(x/i);
p[t]-=get_p(x/i)*(j-i+1),q[t]-=get_q(x/i)*(j-i+1);
}
}
int main(){
#ifdef Devil_Gary
freopen("in.txt","r",stdin);
#endif
T=read(),m=2e6;
phi[1]=mu[1]=1;
pre();
while(T--){
n=read();
memset(vis,0,sizeof vis);
if(n<=m) printf("%lld %lld\n",phi[n],mu[n]);
else solve(n),printf("%lld %lld\n",p[1],q[1]);
}
}