似乎花了几个月大力学了一波数论
然后发现过了几个月又什么都不会了
所以自己推个式子找下感觉
题意 \large\textbf{题意} 题意
求 ∑ i = 1 N − 1 ∑ j = i + 1 N ( i , j ) \sum\limits_{i=1}^{N-1} \sum\limits_{j=i+1}^N (i,j) i=1∑N−1j=i+1∑N(i,j)
多组询问,保证答案在long long范围内
思路 \large\textbf{思路} 思路
根据数据范围大概可以判断出这个题需要预处理
以前做过一道求
∑
i
=
1
n
(
i
,
n
)
\sum\limits_{i=1}^n(i,n)
i=1∑n(i,n) 的题,发现这道题也可以转化成这个东西
∑
i
=
1
N
−
1
∑
j
=
i
+
1
N
(
i
,
j
)
=
∑
i
=
2
N
(
∑
j
=
1
i
−
1
(
i
,
j
)
)
\sum\limits_{i=1}^{N-1} \sum\limits_{j=i+1}^N(i,j)=\sum\limits_{i=2}^N \left(\sum\limits_{j=1}^{i-1}(i,j)\right)
i=1∑N−1j=i+1∑N(i,j)=i=2∑N(j=1∑i−1(i,j))
于是就转化成功了,我们只要能够快速求出后面的式子,就很容易用前缀和解决多组询问的问题
挂个链接吧,这个子问题其实就是这道题
兔队的题解告诉我们子问题可以
Θ
(
n
)
\Theta(\sqrt n)
Θ(n) 解决,但是这题有多组询问,所以这种方法并不占优势
于是用常规方法,
∑
i
=
1
n
(
i
,
n
)
=
∑
i
∣
n
i
×
∑
j
=
1
n
[
(
j
,
n
)
=
=
i
]
\sum\limits_{i=1}^n(i,n)=\sum_{i\mid n}i\times\sum\limits_{j=1}^n[(j,n)==i]
i=1∑n(i,n)=∑i∣ni×j=1∑n[(j,n)==i]
发现
(
j
,
n
)
=
i
(j,n)=i
(j,n)=i 相当于
(
j
/
i
,
n
/
i
)
=
1
(j/i,n/i)=1
(j/i,n/i)=1
所以
∑
j
=
1
n
[
(
j
,
n
)
=
=
i
]
\sum\limits_{j=1}^n[(j,n)==i]
j=1∑n[(j,n)==i] 相当于与
n
/
i
n/i
n/i 互质的数的个数
这时就可以把欧拉函数弄进去了:
∑
i
∣
n
i
×
φ
(
n
/
i
)
\sum_{i\mid n}i\times \varphi(n/i)
∑i∣ni×φ(n/i) 就是答案。
我们预处理一下欧拉函数,然后就可以
Θ
(
n
)
\Theta(\sqrt n)
Θ(n) 算这个式子了
这些操作总的时间复杂度约为
Θ
(
n
n
)
\Theta(n\sqrt n)
Θ(nn)
然后
Θ
(
n
)
\Theta(n)
Θ(n) 弄个前缀和,
Θ
(
1
)
\Theta(1)
Θ(1) 回答每个询问
那么算法的时间复杂度为
Θ
(
n
n
)
\Theta(n\sqrt n)
Θ(nn) ,显然可以通过
不过
Θ
(
n
n
)
\Theta(n\sqrt n)
Θ(nn) 还是太慢了些。事实上有
Θ
(
n
log
n
)
\Theta(n\log n)
Θ(nlogn) 的做法
发现
Θ
(
n
)
\Theta(\sqrt n)
Θ(n) 枚举因数的过程有很多冗余的判断。这就像我们小学二年级的时候判断质数一样
回想一下,在小学三年级
F
l
a
m
i
r
e
\rm F\color{red}lamire
Flamire 教了我们埃氏筛法,它通过枚举因子算贡献的方法,使复杂度降至了
Θ
(
n
log
n
)
\Theta(n \log n)
Θ(nlogn) 。
把埃氏筛法的套路拿过来直接用就行 具体看代码吧
#include<cstdio>
const int N=200000; int n,t,cnt,p[100000];
int v[N+10],phi[N+10]; long long S[N+10];
int main() {
phi[1]=1;
for (int i=2; i<=N; ++i) { //线性筛
v[i]||(p[++cnt]=i,phi[i]=i-1);
for (int j=1; j<=cnt&&1ll*p[j]*i<=N; ++j) {
v[t=p[j]*i]=1; phi[t]=phi[i]*(p[j]-1);
if (!(i%p[j])) { phi[t]+=phi[i]; break; }
}
}
for (int i=1; i<=N; ++i)
for (int j=i+i; j<=N; j+=i) S[j]+=i*phi[j/i]; //算因子贡献
for (int i=3; i<=N; ++i) S[i]+=S[i-1]; //前缀和
while (scanf("%d",&n),n) printf("%lld\n",S[n]); //回答询问
return 0;
}