链接
http://acm.hdu.edu.cn/showproblem.php?pid=5528
题解
今天做完这道题才发现自己数论是如此薄弱
通过观察可以发现,每一行的
0
0
0的个数恰好等于
g
c
d
(
i
,
m
)
gcd(i,m)
gcd(i,m)
打个表发现也确实是如此
于是题目就是要求
a
n
s
=
∑
m
∣
n
m
2
−
∑
m
∣
n
∑
i
=
0
m
−
1
g
c
d
(
i
,
m
)
ans=\sum_{m|n}m^2-\sum_{m|n}\sum_{i=0}^{m-1}gcd(i,m)
ans=m∣n∑m2−m∣n∑i=0∑m−1gcd(i,m)
下面开始推式子
∑
m
∣
n
∑
i
=
1
m
g
c
d
(
i
,
m
)
=
∑
m
∣
n
∑
d
∣
m
m
d
∑
d
∣
i
m
[
g
c
d
(
i
,
m
)
=
d
]
=
∑
m
∣
n
∑
d
∣
m
m
d
φ
(
m
d
)
=
∑
d
∣
n
d
∑
d
∣
m
,
m
∣
d
φ
(
m
d
)
=
∑
d
∣
n
d
∑
m
d
=
k
∣
n
d
φ
(
k
)
=
∑
d
∣
n
d
n
d
=
∑
d
∣
n
n
=
n
σ
0
(
n
)
\sum_{m|n}\sum_{i=1}^mgcd(i,m)\\ =\sum_{m|n}\sum_{d|m}^md\sum_{d|i}^m[gcd(i,m)=d]\\ =\sum_{m|n}\sum_{d|m}^md\varphi({m\over d})\\ =\sum_{d|n}d\sum_{d|m,m|d}\varphi({m\over d})\\ =\sum_{d|n}d\sum_{{m\over d}=k|{n\over d}}\varphi(k)\\ =\sum_{d|n}d{n\over d}\\ =\sum_{d|n}n\\ =n\sigma_0(n)
m∣n∑i=1∑mgcd(i,m)=m∣n∑d∣m∑mdd∣i∑m[gcd(i,m)=d]=m∣n∑d∣m∑mdφ(dm)=d∣n∑dd∣m,m∣d∑φ(dm)=d∣n∑ddm=k∣dn∑φ(k)=d∣n∑ddn=d∣n∑n=nσ0(n)
所以答案就是
σ
2
(
n
)
−
n
σ
0
(
n
)
\sigma_2(n)-n\sigma_0(n)
σ2(n)−nσ0(n)
其中
σ
k
(
n
)
\sigma_k(n)
σk(n)表示
n
n
n的约数的
k
k
k次方和
最坑爹的在于,这道题
O
(
n
)
O(\sqrt n)
O(n)的暴力求会被卡常数
所以用分解质因数来做
设
n
=
∏
p
i
q
i
n=\prod p_i^{q_i}
n=∏piqi
σ
k
(
n
)
=
∏
(
p
i
0
+
p
i
1
+
.
.
.
+
p
i
q
i
)
\sigma_k(n)=\prod(p_i^0+p_i^1+...+p_i^{q_i})
σk(n)=∏(pi0+pi1+...+piqi)
代码
#include <bits/stdc++.h>
#define maxn 100010
#define ull unsigned long long
#define sqr(x) ((x)*(x))
using namespace std;
ull f[maxn], d[maxn], prime[maxn], p[maxn], q[maxn];
bool mark[maxn];
void init()
{
ull i, j, t, cnt;
f[1]=1, d[1]=1;
for(i=2;i<maxn;i++)
{
if(!mark[i])prime[++*prime]=i, f[i]=i*i+1, d[i]=2;
for(j=1;j<=*prime and i*prime[j]<maxn;j++)
{
mark[i*prime[j]]=1;
if(i%prime[j]==0)
{
f[i*prime[j]]=f[i]*prime[j]*prime[j]+1;
for(t=i*prime[j],cnt=0;t%prime[j]==0;t/=prime[j],cnt++);
d[i*prime[j]]=(cnt+1)*d[t];
break;
}
else d[i*prime[j]]=d[i]*2, f[i*prime[j]]=f[i]*f[prime[j]];
}
}
}
ull calc(ull n)
{
ull i, j, ans1=1, ans2=1, t, tot=0, tmp, N=n;
for(i=1;sqr(prime[i])<=n;i++)
if(n%prime[i]==0)for(tot++,p[tot]=prime[i],q[tot]=0;n%prime[i]==0;n/=prime[i],q[tot]++);
if(n>1)p[++tot]=n, q[tot]=1;
for(i=1;i<=tot;i++)
{
for(j=0,t=1,tmp=0;j<=q[i];j++,t*=sqr(p[i]))tmp+=t;
ans1*=tmp;
ans2*=j;
}
return ans1-N*ans2;
}
int main()
{
ull T, n, i;
scanf("%llu",&T);
init();
while(T--)
{
scanf("%llu",&n);
printf("%llu\n",calc(n));
}
return 0;
}