给定序列 A,定义 f(a,b,c) 为 a,b,c 中最小的次小的数的 gcd,求:
n∑i=1n∑j=i+1n∑k=j+1f(ai,aj,ak)
题解
目前来说有两种方法,都十分有启发意义,但是有共同的开头。
考虑到 A 的顺序实际上没有什么关系,那么可以先对 A 排序,使得 i<j<k⟺ai≤aj≤ak,那么此时 f(ai,aj,ak)=gcd(ai,aj),从而答案的式子转化为:
n∑i=1n∑j=i+1gcd(ai,aj)×(n−j)
接下来衍生出了两种做法。
莫比乌斯反演
准确来说应该是欧拉反演?
注意到:
∑d|xφ(d)=x
那么我们可以将目标表达式写为:
n∑i=1n∑j=i+1∑d|ai,ajφ(d)×(n−j)
交换求和顺序,可以得到:
V∑d=1φ(d)n∑i=1n∑j=i+1[d|ai][d|aj](n−j)=V∑d=1φ(d)n∑j=1(n−j)[d|aj]j−1∑i=1[d|ai]
考虑对于每一个 d,我们将满足 d|ai 的数提出来作为集合 S,那么这是容易 O(|S|) 求解的。
接下来我们只需要考虑其复杂度了。
注意到可以认为 τ(n)=O(√n)(在本题值域限制中 τ(n)≤128)也就是说每一个数至多在 128 个 S 中出现,那么我们可以知道 ∑|S|=O(128n),于是复杂度是合理的。
τ(n) 表示 n 的因数个数。
提前预处理一下因数的分解,这是 O(VlogV) 的(由于值域很小,可以直接保存),以及 φ 即可。
总复杂度是 O(VlogV+128n+nlogn)。
另类的枚举
回到基础的式子:
n∑i=1n∑j=i+1gcd(ai,aj)×(n−j)
如果我们固定 j,发现 gcd(ai,aj) 一定是 aj 的因数,并且 aj 的因数至多有 128 个,这启发我们可以直接枚举 d=gcd(ai,aj) 来算 d×cntj×(n−j)。
考虑 cntj 该如何计算,可以开一个桶 butx 记录有多少个满足 x|ai 的数。
发现并不能简单的 cntj=butd,因为这样会算重,考虑将会算重的部分给去掉。
具体来说,发现如果满足 d|gcd(ai,aj),那么 x|d→x|gcd(ai,aj),也就是说 d 会在其所有因数再算一次。直接容斥有点麻烦,所以考虑直接在 but 上进行修改,也就是说进行 z|d,butz←butz−butd 的操作。
注意需要还原 but
于是就不需要容斥了……接着分析复杂度。
枚举因数同上,是 O(128n) 的,然而我们多了一个修改桶的操作,发现与枚举所有数的因数的复杂度是类同的,所以是 O(mlogm) 的。
怎么感觉复杂度假了?
总复杂度就是 O(128n+mlogm+nlogn),差不多。
总结
我首先想到的就是第一种做法……QwQ,数学学多了,但是仍然想了比较长的时间,基础还是不太牢。
第二种做法相对来说要亲民一些,但是比较难拓展到其他题上,也没法析出新的模型(因为完全可以被第一种方法取代),所以说第二种方法惜败。
最后再回顾一下莫反的 5 个经典套路:
- [x=1]=∑d|xμ(d)
- x=∑d|xφ(d)
- 整除分块
- 枚举 gcd
- 枚举 T=kd
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效