【题解】震惊!!!GCD SUM套路竟狂水七倍经验!!!

版权声明:Powered By Fighter https://blog.csdn.net/qq_30115697/article/details/89141016

标题党又来了

题目描述

给定 n n ,求 i = 1 n j = i + 1 n g c d ( i , j ) \sum\limits_{i=1}^{n} \sum\limits_{j=i+1}^{n}gcd(i,j)


解法1:暴力

两个for搞定。每一次计算复杂度为 O ( n 2 ) O(n^2)​

于是你可以水掉UVA11417

解法2:化柿子

既然直接枚举 i , j i,j​ 不行,那么我们换成枚举 g c d gcd​ ,假设我们已经确定了 i , j i,j​ g c d gcd​ k k​ ,那么 g c d ( i / k , j / k ) = 1 gcd(i/k,j/k)=1​ ,于是问题转化为,在 [ 1 , n k ] [1,\lfloor \frac{n}{k} \rfloor]​ 范围内,互质的无序数对个数。

我们接着转化,假设我们已经知道了互质的数中的较大的一个 a a ,那么我们要做的就是求出小于这个数的与它互质的数。想到了什么?欧拉函数!!

于是我们会发现,我们要求的就是在 [ 1 , n k ] [1,\lfloor \frac{n}{k} \rfloor] 范围内的所有数的欧拉函数值得和。

那么最终的答案就是
k = 1 n d = 1 n k ϕ ( d ) \sum_{k=1}^{n} \sum_{d=1}^{\lfloor\frac{n}{k}\rfloor}\phi(d)
然后我们发现,后面一个和可以通过线性筛和前缀和预处理做到 O ( 1 ) O(1) 查询和。于是成功的把每次计算复杂度降到了 O ( n ) O(n)​

于是你可以*掉洛谷1390洛谷2398,还有洛谷2568

不过要注意的是,这些题中的 ϕ ( 1 ) \phi(1)​ 的初始值会有所不同,并且有些题求的是有序数对个数,需要对答案进行一些简单处理。

终极解法:统统预处理

尽管解法2已经将时间复杂度压到了一次计算 O ( n ) O(n)​ ,几乎是最优了,但对于多组数据,尤其是几万甚至几十万的数据组数时,就显得太慢了愚蠢,垃圾

我们发现, O ( n ) O(n) 复杂度的瓶颈在于每次都需要枚举计算。那么我们考虑用预处理把所有的答案都预处理出来,做到 O ( 1 ) O(1) 查询。

继续化柿子:
f ( n ) = i = 1 n 1 g c d ( i , n ) a n s = i = 1 n f ( n ) f ( n ) = d n d × i = 1 n 1 [ g c d ( i , n ) = d ] = d n d × i = 1 n d 1 [ g c d ( i , n d ) = 1 ] = d n d × ϕ ( n d ) \begin{aligned} &设f(n)=\sum_{i=1}^{n-1}gcd(i,n)\\ &那么ans=\sum_{i=1}^{n}f(n)\\ f(n)&=\sum_{d|n}d\times\sum_{i=1}^{n-1}[gcd(i,n)=d]\\ &=\sum_{d|n}d\times\sum_{i=1}^{\frac{n}{d}-1}[gcd(i,\frac{n}{d})=1]\\ &=\sum_{d|n}d\times \phi(\frac{n}{d}) \end{aligned}
那么我们就可以在预处理欧拉函数后,用类似埃氏筛的方法,预处理出 f ( n ) f(n)​ ,最后对其做一遍前缀和,就得到了最终的答案。

此处给出筛 f ( n ) f(n) 的代码:

for(int i = 1; i*i < MAX; i++){
	ans[i*i] += i*phi[i];
	for(int j = i+1; i*j < MAX; j++){
		ans[i*j] += phi[j]*i+phi[i]*j;
	}
}

由于 d d n d \frac{n}{d} 是对称的,我们直接一次累计即可,但对于平方数,只能累计一个答案。

于是你又可以A掉SP3871UVA11424UVA11426.


至此,恭喜你成功用1个套路水掉了七倍经验!!

猜你喜欢

转载自blog.csdn.net/qq_30115697/article/details/89141016