题意简述
有
3
e
5
3e5
3e5组询问,每次给定一个
n
(
n
<
=
1000000
)
n(n<=1000000)
n(n<=1000000),求:
l
c
m
(
1
,
n
)
+
l
c
m
(
2
,
n
)
.
.
.
+
l
c
m
(
n
,
n
)
lcm(1,n)+lcm(2,n)...+lcm(n,n)
lcm(1,n)+lcm(2,n)...+lcm(n,n)
数据
输入
T //询问个数
n
n
...
n//每个n
输出
ans
ans
...
ans//对于每个询问,输出答案
思路
暴力推式子。来♂
还是设
g
=
g
c
d
(
i
,
n
)
g=gcd(i,n)
g=gcd(i,n),注意不是常数,是宏定义,相当于#define
原
式
=
∑
i
=
1
n
l
c
m
(
i
,
n
)
=
∑
i
=
1
n
n
i
g
=
n
∑
i
=
1
n
i
g
(
常
规
变
形
,
拆
l
c
m
为
g
c
d
)
=
n
∑
d
∣
n
∑
i
=
1
n
[
g
=
=
d
]
i
d
(
枚
举
g
c
d
的
值
,
去
判
断
在
哪
些
i
中
被
算
到
)
=
n
∑
d
∣
n
∑
i
=
1
n
[
g
c
d
(
i
d
,
n
d
)
=
=
1
]
i
d
(
括
号
里
面
除
以
d
)
=
n
∑
d
∣
n
∑
i
=
1
n
d
[
g
c
d
(
i
,
n
d
)
=
=
1
]
i
(
把
i
的
定
义
换
成
:
枚
举
i
d
)
=
n
∑
d
∣
n
∑
i
=
1
d
[
g
c
d
(
i
,
d
)
=
=
1
]
i
(
因
为
我
们
枚
举
的
d
是
n
的
因
数
,
所
以
d
和
n
/
d
应
该
是
对
称
的
,
把
n
/
d
换
成
d
,
只
是
枚
举
顺
序
反
了
一
下
,
不
改
变
答
案
)
原式\\ =\sum\limits_{i=1}^{n}lcm(i,n)\\ =\sum\limits_{i=1}^{n}\frac{ni}{g}\\ =n\sum\limits_{i=1}^{n}\frac{i}{g}\\ (常规变形,拆lcm为gcd)\\ =n\sum\limits_{d|n}\sum\limits_{i=1}^{n}[g==d]\frac{i}{d}\\ (枚举gcd的值,去判断在哪些i中被算到)\\ =n\sum\limits_{d|n}\sum\limits_{i=1}^{n}[gcd(\frac{i}{d},\frac{n}{d})==1]\frac{i}{d}\\ (括号里面除以d)\\ =n\sum\limits_{d|n}\sum\limits_{i=1}^{\frac{n}{d}}[gcd(i,\frac{n}{d})==1]i\\ (把i的定义换成:枚举\frac{i}{d})\\ =n\sum\limits_{d|n}\sum\limits_{i=1}^{d}[gcd(i,d)==1]i\\ (因为我们枚举的d是n的因数,所以d和n/d应该是对称的,把n/d换成d,只是枚举顺序反了一下,不改变答案)
原式=i=1∑nlcm(i,n)=i=1∑ngni=ni=1∑ngi(常规变形,拆lcm为gcd)=nd∣n∑i=1∑n[g==d]di(枚举gcd的值,去判断在哪些i中被算到)=nd∣n∑i=1∑n[gcd(di,dn)==1]di(括号里面除以d)=nd∣n∑i=1∑dn[gcd(i,dn)==1]i(把i的定义换成:枚举di)=nd∣n∑i=1∑d[gcd(i,d)==1]i(因为我们枚举的d是n的因数,所以d和n/d应该是对称的,把n/d换成d,只是枚举顺序反了一下,不改变答案)
现在我们要求这样一个东西:
对于任意一个
<
=
n
<=n
<=n的数
x
x
x,求
[
1
,
x
]
[1,x]
[1,x]中所有和
x
x
x互质的数的和。
我们能求什么呢?
[
1
,
x
]
[1,x]
[1,x]中所有和
x
x
x互质的数的个数。很明显这个就是
ϕ
(
x
)
\phi(x)
ϕ(x)。
珂是。。。如果变成求和。。。怎么办呢?好像不太好求。
我们回想一下高斯是怎么算
[
1
,
100
]
[1,100]
[1,100]的和的。显然他知道
[
1
,
100
]
[1,100]
[1,100]里面的个数(
100
100
100个)。然后他很聪明,把序列反过来写一遍,发现和相等,这样和就是
100
∗
101
/
2
100*101/2
100∗101/2。
珂是我为什么要讲高斯的故事呢?因为这个和高斯很像。我们不妨设
x
=
20
x=20
x=20,观察一下和
20
20
20互质的数,把逆序,逆序+正序,都写在下面:
1 ,3 , 7, 9,11,13,17,19
19,17,13,11, 9, 7, 3, 1
20,20,20,20,20,20,20,20
发现了什么?当然,如果你把
x
x
x代入别的验证,也是这样的。这就是我们发现的规律。
此时,两个序列的和就是
n
∗
ϕ
(
n
)
n*\phi(n)
n∗ϕ(n)。显然,两个序列的和都是相等的。那么其中一个序列的和就是:
n
∗
ϕ
(
n
)
2
\frac{n*\phi(n)}{2}
2n∗ϕ(n)。也就是我们要求的那个东西。
(不过要特判一个
n
=
1
n=1
n=1,此时的和为
1
1
1,而不是
n
∗
p
h
i
(
n
)
/
2
=
1
2
n*phi(n)/2=\frac{1}{2}
n∗phi(n)/2=21)
还记得我们刚刚的答案式子是什么吗?是
n
∑
d
∣
n
∑
i
=
1
d
[
g
c
d
(
i
,
d
)
=
=
1
]
i
n\sum\limits_{d|n}\sum\limits_{i=1}^{d}[gcd(i,d)==1]i
nd∣n∑i=1∑d[gcd(i,d)==1]i。然后我们现在珂以把后面优化掉了,变成:
n
∑
d
∣
n
d
∗
ϕ
(
d
)
2
n\sum\limits_{d|n}\frac{d*\phi(d)}{2}
nd∣n∑2d∗ϕ(d)
至此,我们珂以每个询问
O
(
n
)
O(\sqrt{n})
O(n)加特技,卡常数,水过这个题。
当然,我们也有更快的方法。考虑预处理。设一个数组,一开始都是
0
0
0。枚举每个
d
d
d和
d
d
d的倍数,在
d
d
d的倍数上加上
d
∗
ϕ
(
d
)
2
\frac{d*\phi(d)}{2}
2d∗ϕ(d)的值(特判
d
=
1
d=1
d=1)。虽然这样预处理是
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)的(调和级数),但是每次询问就珂以
O
(
1
)
O(1)
O(1)出来了。相比刚刚那个算法,虽然多耗了一点思考,但是省了很多卡常数的力气,珂以说是优秀的三好算法了。
(三好算法)代码:
#include<bits/stdc++.h>
#define N 1001000
using namespace std;
bool notp[N];int primes[N],phi[N];
long long f[N];
void Init()
{
int& cnt=primes[0];
int n=1000000;
notp[1]=1;
phi[1]=1;
for(int i=2;i<=n;i++)
{
if (!notp[i])
{
primes[++cnt]=i;
phi[i]=i-1;
}
for(int j=1;j<=cnt and i*primes[j]<=n;j++)
{
int u=primes[j];
notp[i*u]=1;
if (i%u!=0)
{
phi[i*u]=phi[i]*phi[u];
}
else
{
phi[i*u]=phi[i]*u;
break;
}
}
}//预处理phi
for(int i=1;i<=n;i++)
{
for(int j=1;i*j<=n;j++)
{
f[i*j]+=(i==1?1:1ll*phi[i]*i/2);
}
}//预处理,O(nlogn)
}
int n;
int main()
{
Init();
int T;scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
printf("%lld\n",f[n]*1ll*n);
}
return 0;
}
//代码是很久之前写的,好久不整理博客了。所以码风有点。。。。。。。
//虽然常数小,但也是卡了一些常数,比如我没有全部用long long