您的位置:首页 > 其它

bzoj2226 [Spoj 5971] LCMSum(反演(互质数之和的公式))

2017-12-29 18:34 686 查看

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

Sample Output

1

4

55

HINT

1 <= T <= 300000

1 <= n <= 1000000


[Submit][Status][Discuss]

分析:

感觉牵扯到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
;
int sshu
,tot=0,n;
bool no
;

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
;

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
);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: