令所求的式子为
(f(n)=Sigma_iSigma_jmu(ij))
则可以写出如下递推式,画个 (n*n) 的表格可辅助理解
(f(n)=f(n-1)+2Sigma_{x=1}^nmu(xn))
考虑 (Sigma_{x=1}^nmu(xn)) 的含义,这里先直接给出结果:
(Sigma_{x=1}^nmu(xn)=Sigma_{x=1}^n[gcd(x,n)=1]*mu(x)*mu(n))
上式的含义最简洁的一种理解方式是,由于莫比乌斯函数是积性的,因此当二者gcd为1时,有 (mu(xn)=mu(x)mu(n));而不为1的时候 (xn) 一定有平方因子,因此乘起来整个都是0。因此,可以写成上式。
使用经典结论 ([dog=1]=Sigma_{d|dog}mu(d)) 可以继续得到:
(Sigma_{x=1}^nmu(xn)=Sigma_{x=1}^n[gcd(x,n)=1]*mu(x)*mu(n))
(=mu(n)Sigma_{x=1}^nmu(x)Sigma_{d|gcd(x,n)}mu(d))
交换求和次序
(=Sigma_{d|n}mu(d)*Sigma_{d|x,xleq n}mu(x))
此时似乎很难往下化简了,我也一度陷入了懵逼当中,但当我冷静下来时,我发现化简到这一步已经够用了,因为上式直接计算似乎是(好吧我瞎猜的,应该差不多)个 (O(sqrt{n}*log(n))) 的复杂度,所以就直接计算就好了。
这样,总的程序复杂度就是 (O(nsqrt{n}*log(n))) ,可以写了。
具体实现的时候,要预处理出每个数的所有因数,并且要在发现某 (mu) 值为零时立刻continue剪个枝才能通过此题。
代码:
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,a,n) for(int i=n;i>=a;i--)
#define pb push_back
#define SZ(x) ((int)(x).size())
#define fastin ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
typedef long long ll;
typedef pair<int,int> pii;
typedef double db;
const int N=2e5+5;
int tot,flg[N+5],p[N+5],mu[N+5];
void getMu() {
mu[1] = 1;
for (int i = 2; i <= N; ++i) {
if (!flg[i]) p[++tot] = i, mu[i] = -1;
for (int j = 1; j <= tot && i * p[j] <= N; ++j) {
flg[i * p[j]] = 1;
if (i % p[j] == 0) {
mu[i * p[j]] = 0;
break;
}
mu[i * p[j]] = -mu[i];
}
}
}
int ans[50010],t,n;
vector<int> fac[50010];
int main(){
getMu();
ans[1]=1;
rep(i,1,50000){
for(int j=i;j<=50000;j+=i){
fac[j].pb(i);
}
}
rep(i,2,50000){
if(!mu[i]){
ans[i]=ans[i-1];continue;
}
int cof=2*mu[i];
int sum=0;
for(auto d:fac[i]){
if(!mu[d]) continue;
int tmp=0;
for(int x=d;x<i;x+=d){
tmp+=mu[x];
}
tmp*=mu[d];
sum+=tmp;
}
ans[i]=ans[i-1]+cof*sum;
}
cin>>t;
while(t--){
cin>>n;
printf("%d
",ans[n]);
}
return 0;
}