享受推式子的乐趣吧
数论真有趣!
庆祝:数论紫题第 (3) 道。
[sum_{i=1}^n operatorname{lcm}(i,n)
]
[= sum_{i=1}^n frac{i imes n}{gcd(i,n)}
]
[= n imes sum_{i=1}^n frac{i}{gcd(i,n)}
]
[= n imes sum_{d|n} sum_{i=1}^n frac{i}{d} [gcd(i,n) == d]
]
[= n imes sum_{d|n} sum_{i=1}^{frac{n}{d}} i [gcd(i,frac{n}{d}) == 1]
]
[= n imes sum_{d|n} sum_{i=1}^d i [gcd(i,d)==1]
]
(注:由于 (d) 是枚举因数,因数成对出现,所以 (frac{n}{d}) 等同于 (d)).
[= n imes sum_{d|n} frac{d}{2} phi_d
]
感觉,数论上大部分是 gcd,然后就是欧拉筛
欧拉筛 日常一下,然后可以提前计算答案。(类似于打表?)
时间复杂度: (O(n imes d)). ((d) 的值之后解释)
空间复杂度: (O(n)).
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N1=1e6+1;
const int N=1e6;
inline ll read(){char ch=getchar();int f=1;while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
ll x=0;while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;}
int T; ll ans[N1];
ll n,f=0;
ll phi[N1],prime[N1];
inline void Euler() {
phi[1]=1;
for(int i=2;i<=N;i++) {
if(!phi[i]) prime[++f]=i,phi[i]=i-1;
for(int j=1;j<=f && i*prime[j]<=N;j++) {
if(i%prime[j]==0) {
phi[i*prime[j]]=phi[i]*prime[j];
break;
}
phi[i*prime[j]]=phi[i]*(prime[j]-1);
}
}
} //欧拉筛模板
int main(){
T=read(); Euler();
for(int i=1;i<=N;i++)
for(int j=i;j<=N;j+=i) ans[j]+=phi[j/i]*(j/i)+1>>1;
while(T--) {
n=read();
printf("%lld
",n*ans[n]);
}
return 0;
}
下面分析一下时间复杂度。
你会发现,除了这一段:
for(int i=1;i<=N;i++)
for(int j=i;j<=N;j+=i) ans[j]+=phi[j/i]*(j/i)+1>>1;
较难计算时间,其余都是 (O(n)).
那么这一段的时间,我们再来推个式子:
[= sum_{i=1}^n frac{n}{i}
]
[= n imes (sum_{i=1}^n frac{1}{i})
]
这时,你可能想到了 欧拉调和级数 ,但这里 (n) 是有限的。
式子推不下去,我们就打了个暴力。
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
inline int read(){char ch=getchar();int f=1;while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
int x=0;while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;}
int main(){
double x=0;
for(int i=1;i<=1000000;i++) x+=1.0/i;
cout<<x;
return 0;
}
最终结果: (14.3927)
那么,这可以认为是较小的一个常数(因为它不会影响程序通过本题)。
所以,最终的时间复杂度为: (O(n)).
实际得分: (100pts).