标题党又来了
题目描述
给定
n,求
i=1∑nj=i+1∑ngcd(i,j)。
解法1:暴力
两个for
搞定。每一次计算复杂度为
O(n2)。
于是你可以水掉UVA11417。
解法2:化柿子
既然直接枚举
i,j不行,那么我们换成枚举
gcd,假设我们已经确定了
i,j的
gcd为
k,那么
gcd(i/k,j/k)=1,于是问题转化为,在
[1,⌊kn⌋]范围内,互质的无序数对个数。
我们接着转化,假设我们已经知道了互质的数中的较大的一个
a,那么我们要做的就是求出小于这个数的与它互质的数。想到了什么?欧拉函数!!
于是我们会发现,我们要求的就是在
[1,⌊kn⌋]范围内的所有数的欧拉函数值得和。
那么最终的答案就是
k=1∑nd=1∑⌊kn⌋ϕ(d)
然后我们发现,后面一个和可以通过线性筛和前缀和预处理做到
O(1)查询和。于是成功的把每次计算复杂度降到了
O(n)。
于是你可以*掉洛谷1390和洛谷2398,还有洛谷2568。
不过要注意的是,这些题中的
ϕ(1)的初始值会有所不同,并且有些题求的是有序数对个数,需要对答案进行一些简单处理。
终极解法:统统预处理
尽管解法2已经将时间复杂度压到了一次计算
O(n),几乎是最优了,但对于多组数据,尤其是几万甚至几十万的数据组数时,就显得太慢了愚蠢,垃圾。
我们发现,
O(n)复杂度的瓶颈在于每次都需要枚举计算。那么我们考虑用预处理把所有的答案都预处理出来,做到
O(1)查询。
继续化柿子:
f(n)设f(n)=i=1∑n−1gcd(i,n)那么ans=i=1∑nf(n)=d∣n∑d×i=1∑n−1[gcd(i,n)=d]=d∣n∑d×i=1∑dn−1[gcd(i,dn)=1]=d∣n∑d×ϕ(dn)
那么我们就可以在预处理欧拉函数后,用类似埃氏筛的方法,预处理出
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和
dn是对称的,我们直接一次累计即可,但对于平方数,只能累计一个答案。
于是你又可以A掉SP3871,UVA11424和UVA11426.
至此,恭喜你成功用1个套路水掉了七倍经验!!