BZOJ传送门
洛谷传送门
SPOJ传送门
解析:
重温开通博客之前写的一些数学题。
现在还是推了我一段时间的。
思路:
首先我们并不擅长处理 L C M LCM LCM问题,所以一律换成 g c d gcd gcd
∑ i = 1 n l c m ( i , n ) = ∑ i = 1 n i × n g c d ( i , n ) \sum_{i=1}^{n}lcm(i,n)=\sum_{i=1}^n\frac{i\times n}{gcd(i,n)} i=1∑nlcm(i,n)=i=1∑ngcd(i,n)i×n
接下来需要巧妙的转化:
A
n
s
=
∑
i
=
1
n
i
×
n
g
c
d
(
i
,
n
)
=
1
2
(
∑
i
=
1
n
−
1
i
×
n
g
c
d
(
i
,
n
)
+
∑
i
=
1
n
−
1
(
n
−
i
)
∗
n
g
c
d
(
n
−
i
,
n
)
)
+
n
\begin{aligned} Ans=&\sum_{i=1}^{n}\frac{i\times n}{gcd(i,n)}\\ =&\frac{1}2(\sum_{i=1}^{n-1}\frac{i\times n}{gcd(i,n)}+\sum_{i=1}^{n-1}\frac{(n-i)*n}{gcd(n-i,n)})+n \end{aligned}
Ans==i=1∑ngcd(i,n)i×n21(i=1∑n−1gcd(i,n)i×n+i=1∑n−1gcd(n−i,n)(n−i)∗n)+n
由于 g c d ( i , n ) = g c d ( n − i , n ) gcd(i,n)=gcd(n-i,n) gcd(i,n)=gcd(n−i,n),(不要告诉我你不会 O ( log n ) O(\log n) O(logn)求 g c d gcd gcd)
所以我们进一步化简: A n s = 1 2 ∑ i = 1 n − 1 n 2 g c d ( i , n ) + n \begin{aligned} Ans=\frac{1}2\sum_{i=1}^{n-1}\frac{n^2}{gcd(i,n)}+n \end{aligned} Ans=21i=1∑n−1gcd(i,n)n2+n
现在我们只需要考虑怎么求这个东西: ∑ i = 1 n − 1 n 2 g c d ( i , n ) \sum_{i=1}^{n-1}\frac{n^2}{gcd(i,n)} i=1∑n−1gcd(i,n)n2
套路:枚举
g
c
d
gcd
gcd
A
n
s
=
∑
d
∣
n
∑
i
=
1
n
−
1
n
2
d
[
g
c
d
(
i
,
n
)
=
d
]
=
∑
d
∣
n
n
2
d
∑
i
=
1
n
−
1
[
g
c
d
(
i
,
n
)
=
d
]
\begin{aligned} Ans=&\sum_{d\mid n}\sum_{i=1}^{n-1}\frac{n^2}{d}[gcd(i,n)=d]\\ =&\sum_{d\mid n}\frac{n^2}d\sum_{i=1}^{n-1}[gcd(i,n)=d] \end{aligned}
Ans==d∣n∑i=1∑n−1dn2[gcd(i,n)=d]d∣n∑dn2i=1∑n−1[gcd(i,n)=d]
。。。
。。。
。。。
这么明显的欧拉函数woc。。。
A n s = ∑ d ∣ n , d ̸ = n n 2 × ϕ ( n d ) d = n ∑ d ∣ n , d ̸ = 1 d × ϕ ( d ) \begin{aligned} Ans=&\sum_{d\mid n,d\not=n}\frac{n^2\times \phi(\frac{n}d)}{d}\\ =&n\sum_{d\mid n,d\not=1}d\times \phi(d) \end{aligned} Ans==d∣n,d̸=n∑dn2×ϕ(dn)nd∣n,d̸=1∑d×ϕ(d)
我们只需要筛出欧拉函数,然后枚举倍数加到该加的地方,复杂度是调和级数。相当于 O ( n log n ) O(n\log n) O(nlogn)。
然后就可以 O ( 1 ) O(1) O(1)回答每个询问了
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc get_char
#define pc putchar
#define cs const
namespace IO{
namespace IOONLY{
cs int Rlen=1<<18|1;
char buf[Rlen],*p1,*p2;
}
inline char get_char(){
using namespace IOONLY;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
inline int getint(){
re int num;
re char c;
while(!isdigit(c=gc()));num=c^48;
while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
return num;
}
inline void outint(ll a){
static char ch[23];
if(a==0)pc('0');
while(a)ch[++ch[0]]=a-a/10*10,a/=10;
while(ch[0])pc(ch[ch[0]--]^48);
}
}
using namespace IO;
cs int P=1000006;
int phi[P],prime[P],pcnt;
bool mark[P];
ll ans[P];
inline void linear_sieves(int len=P-6){
phi[1]=1;
for(int re i=2;i<=len;++i){
if(!mark[i])prime[++pcnt]=i,phi[i]=i-1;
for(int re j=1;i*prime[j]<=len;++j){
mark[i*prime[j]]=true;
if(i%prime[j]==0){phi[i*prime[j]]=phi[i]*prime[j];break;}
phi[i*prime[j]]=phi[i]*(prime[j]-1);
}
}
for(int re i=1;i<=len;++i)
for(int re j=2;j*i<=len;++j)ans[i*j]+=(ll)j*phi[j];
for(int re i=1;i<=len;++i)ans[i]=ans[i]*i/2+i;
}
signed main(){
linear_sieves();
for(int re T=getint();T--;pc('\n'))outint(ans[getint()]);
return 0;
}