math
求 S(n) = ∑i∈[1,n]∑j∈[1,n] F(i,j),n<=109
F(i,j) = ∑d∈[1,i*j] [d | i*j]
1. 化简S(n)
step 1.
F(i,j) = ∑d∈[1,i*j] [d/gcd(i,d) | i*j/gcd(i,d)]
= ∑d∈[1,i*j] [d/gcd(i,d) | j]
step 2.
S(n) = ∑i∈[1,n]∑j∈[1,n]∑d∈[1,i*j] [d/gcd(i,d) | j]
= ∑i∈[1,n]∑d∈[1,i*n] [n/(d/gcd(i,d)]
= ∑i∈[1,n]∑d∈[1,i*n] [n*gcd(i,d)/d]
step 3.
S(n) = ∑i∈[1,n]∑d∈[1,i*n]∑k [gcd(i,d)==k]*[n*k/d]
= ∑k∑i∈[1,n/k]∑d∈[1,n] [gcd(i,d)==1]*[n/d]
S(n) = ∑i∈[1,n]∑d∈[1,n]∑k∈[1,n/i] [gcd(i,d)==1]*[n/d]
= ∑i∈[1,n]∑d∈[1,n] [gcd(i,d)==1]*[n/d]*[n/i]
S(n) = ∑i∈[1,n]∑j∈[1,n] [gcd(i,j)==1]*[n/i]*[n/j]
step 4.
S(n) = ∑d μ(d)*∑i∈[1,n/d] ∑j∈[1,n/d] [n/(id)]*[n/(jd)]
= ∑d μ(d)*∑i∈[1,n/d] [n/(id)]*∑j∈[1,n/d] [n/(jd)]
令g(n) = ∑i∈[1,n] [n/i],则S(n) = ∑d μ(d)*g([n/d])2
只计算所有不同的[n/d],可以做到O(n2/3)
2.0 计算M(n) = ∑i∈[1,n] μ(i)
M(n) = ∑i∈[1,n] μ(i)
= ∑i∈[1,n] (∑d|i μ(d) - ∑d|i,d<i μ(d))
= 1 - ∑i∈[1,n]∑d|i,d<i μ(d)
= 1 - ∑d|i,d<i∑i∈[1,n] μ(d)
= 1 - ∑d∈[1,n] ∑i∈[2,n/d] μ(d)
= 1 - ∑i∈[2,n] ∑d∈[1,n/i] μ(d)
= 1 - ∑i∈[2,n] M([n/i])
筛出n2/3以内的M,查询的时候查表O(1)回答.
对于大于n2/3的情况,我们用hash表来记忆化.
这样能做到O(n2/3)的复杂度.
2.1 计算F(n) = ∑i∈[1,n] phi(i)
由n = ∑d|n phi(d) ,我们可以类似地得到F(n)的计算方法
F(n) = ∑i∈[1,n] phi(i)
= ∑i∈[1,n] (∑d|i phi(d) - ∑d|i,d<i phi(d))
= n(n+1)/2 - ∑i∈[1,n]∑d|i,d<i phi(d)
= n(n+1)/2 - ∑i∈[2,n] F([n/i])
3. 码码码
#include <vector>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define MAXN 1000005
#define SIZE ((1<<18)-1)
#define Mod 1000000007
using namespace std;
typedef long long ll;
namespace Mobius{
bool ban[MAXN];
vector<int>p;
int n,mu[MAXN],ptot;
inline void linear_shake(int n){
mu[1]=1;
for(int i=2;i<=n;++i){
if(!ban[i]){
++ptot;
p.push_back(i);
mu[i]=Mod-1;
}
for(int j=0,k;j<ptot;++j){
if((ll)i*p[j]>n)break;
ban[k=i*p[j]]=1;
if(i%p[j]){
mu[k]=Mod-mu[i];
}
else{
mu[k]=0;
break;
}
}
}
for(int i=2;i<=n;++i){
mu[i]=(mu[i]+mu[i-1])%Mod;
}
}
int set[MAXN],f[MAXN];
int head[SIZE+1],tot,next[MAXN];
inline int M(int x){
if(!ptot)linear_shake(n=1000000);
if(x<=n)return mu[x];
int p=x&SIZE;
for(int i=head[p];i;i=next[i])
if(set[i]==x){
return f[i];
}
int res=1;
for(int i=2,j;i<=x;i=j+1){
j=x/(x/i);
res=(res+Mod-ll(j-i+1)*M(x/i)%Mod)%Mod;
}
int o=++tot;
set[o]=x,f[o]=res;
next[o]=head[p],head[p]=o;
return res;
}
};
inline void set_IO(){
freopen("math.in","r",stdin);
freopen("math.out","w",stdout);
}
inline int g(int n){
int res=0;
for(int i=1,j;i<=n;i=j+1){
j=n/(n/i);
res=(res+ll(j-i+1)*(n/i)%Mod)%Mod;
}
return res;
}
int n,ans;
int main(){
set_IO();
scanf("%d",&n);
for(int i=1,j,k;i<=n;i=j+1){
j=n/(n/i);
k=g(n/i),k=(ll)k*k%Mod;
k=(ll)k*((Mobius::M(j)+Mod-Mobius::M(i-1))%Mod)%Mod;
ans=(ans+k)%Mod;
}
printf("%d\n",ans);
return 0;
}