Description
Given n, calculate the sum LCM(1,n) + LCM(2,n) + .. + LCM(n,n), where LCM(i,n) denotes the Least Common Multiple of the integers i and n.
Input
The first line contains T the number of test cases. Each of the next T lines contain an integer n.
Output
Output T lines, one for each test case, containing the required sum.
Sample Input
3
1
2
5
1
2
5
Sample Output
1
4
55
4
55
HINT
1 <= T <= 300000
1 <= n <= 1000000
分析:
感觉牵扯到LCM的式子都比较难化
这里我稍微解释一下这个式子:
现在我们的问题就是求解:
设:
这个式子的定义就是:1~n中所有与n互质的数之和
关于这个问题,有一个公式(这里就不推导了):
其中g(1)=1
(请各位做好笔记,直接记住这个公式)
枚举约数d的复杂度为sqrt(n)
#include<cstdio>
#include<cstring>
#include<iostream>
#define ll long long
using namespace std;
const int N=1000003;
ll phi[N];
int sshu[N],tot=0,n;
bool no[N];
void make()
{
for (int i=2;i<N;i++)
{
if (!no[i])
sshu[++tot]=i;
for (int j=1;j<=tot&&sshu[j]*i<N;j++)
{
no[sshu[j]*i]=1;
if (i%sshu[j]==0) break;
}
}
for (int i=1;i<N;i++) phi[i]=(ll)i;
for (int i=1;i<=tot;i++)
for (int j=sshu[i];j<N;j+=sshu[i])
{
phi[j]=(ll)phi[j]/sshu[i];
phi[j]=(ll)phi[j]*(sshu[i]-1);
}
for (int i=2;i<N;i++)
phi[i]=(ll)phi[i]*i/2;
}
int main()
{
make();
int T;
scanf("%d",&T);
while (T--)
{
scanf("%d",&n);
ll ans=0;
for (int i=1;i*i<=n;i++)
if (n%i==0)
{
ans=ans+phi[n/i];
if (n/i!=i) ans=ans+phi[i];
}
printf("%lld\n",(ll)n*ans); //不要忘了*n
}
return 0;
}
把上面的代码交上去,险些T掉
因为题目中的询问非常多,我们应该有一个意识:把答案组织成便于回答询问的形式
实际上我们不用每读入一个n之后再枚举ta的约数
我们可以在一开始就预处理好,这样就可以O(1)回答询问了
(我们还可以把phi的计算放到线性筛中,进一步优化时间)
(我的代码竟然以微弱优势踩了GXZlegend)
void make()
{
phi[1]=1;
for (int i=2;i<N;i++)
{
if (!no[i])
{
sshu[++tot]=i;
phi[i]=(ll)i-1; //素数
}
for (int j=1;j<=tot&&sshu[j]*i<N;j++)
{
no[sshu[j]*i]=1;
if (i%sshu[j]==0)
{
phi[i*sshu[j]]=(ll)phi[i]*sshu[j]; //积性函数
break;
}
phi[i*sshu[j]]=(ll)phi[i]*phi[sshu[j]];
}
}
for (int i=2;i<N;i++)
phi[i]=(ll)phi[i]*i/2;
}
ll ans[N];
int main()
{
make();
for (int i=1;i<N;i++)
for (int j=i;j<N;j+=i)
ans[j]+=phi[i];
int T;
scanf("%d",&T);
while (T--)
{
scanf("%d",&n);
printf("%lld\n",n*ans[n]);
}
return 0;
}