题目描述
有一天,TIBBAR和LXL比赛谁先算出1~N这N个数中每任意两个不同的数的最大公约数的和。LXL还在敲一个复杂而冗长的程序,争取能在100s内出解。而TIBBAR则直接想1s秒过而获得完胜,请你帮他完成这个任务。
输入输出格式
输入格式:
共一行,一个正整数N。
输出格式:
共一行,一个数,为1~N这N个数中每任意两个不同的数的最大公约数的和。
输入输出样例
输入样例#1:
10
输出样例#1:
67
说明
对于40%的数据,2≤N≤2000.
对于100%的数据,2≤N≤2000000.
以此题祭我小学生般的数论水平
显而易见,这道题让我们求这个
∑i=n1∑j=i−11gcd(i,j)
显然有O(n2)的做法,然而没什么用
我们考虑一下怎么优化。
我们来考虑一下后半部分怎么化简,我们可以从枚举i和j,转换为枚举gcd的值,我们可以设gcd(i,j)=k所以k一定是i的因数,那么我们设g(i,k)为∑j=i−11gcd(i,j)=k的j的个数我们再想一下g函数怎么求gcd(i,j)=k等价于是gcd(⌊ik⌋,⌊jk⌋)=1那么⌊jk⌋的个数=φ(⌊ik⌋)那么我们的式子就成了∑i||(i是n的因数)φ(⌊ik⌋)∗iφ函数可以筛出来,同理因为i是n的因数,我们也可以筛出来
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2000005;
typedef long long ll;
int prime[150000],euler[maxn],cntForPrime,n;
ll f[maxn];
bool isNP[maxn];
void initE()
{
for(int i=2;i<=n;i++)
{
if(!isNP[i])
{
prime[++cntForPrime]=i;
euler[i]=i-1;
}
for(int j=1;j<=cntForPrime;j++)
{
if(i*prime[j]<=n)
{
isNP[i*prime[j]]=1;
if(i%prime[j]==0)
{
euler[i*prime[j]]=euler[i]*prime[j];
break;
}
else
{
euler[i*prime[j]]=(prime[j]-1)*euler[i];
}
}
else
{
break;
}
}
}
}
void debug(ll *arr,int n)
{
for(int i=1;i<=n;i++)
{
cout<<arr[i]<<" ";
}
cout<<"\n";
}
void init()
{
cin>>n;
initE();
// debug(euler,n);
//debug(euler,n);
}
void solve()
{
for(int i=1;i<=n;i++)
{
for(int j=i;j<=n;j+=i)
{
f[j]+=euler[j/i]*i;
}
}
}
int main()
{
init();
solve();
//debug(f,n);
ll sum=0;
for(int i=1;i<=n;i++)sum+=f[i];
cout<<sum;
return 0;
}