Codeforces Round 911 (Div. 2) D. Small GCD

题目链接:https://codeforces.com/contest/1900/problem/D

对于已经排序好的数组 a,我们需要计算:

i=1nj=i+1ngcd(ai,aj)(nj)

由于 d|nϕ(d)=n,因此:

gcd(ai,aj)=d|ai,d|ajψ(d)

代入可得:

i=1nj=i+1nd|ai,d|ajψ(d)(nj)

因此,枚举到 aj 时,遍历它的每个因数 d,看之前有多少个 ai 有因数 d,乘上 ψ(d)(nj) 即可。

求多个数欧拉函数的方法有很多,可以用埃筛或者线性筛。

埃筛递推思路:对于每个质数 p,把它的倍数都乘上 (p1)/p 即可。

线性筛递推思路,对于质数 p 有:

  • pn 的因子,但 p2 不是 n 的因子,那么 ψ(n)=ψ(n/p)p
  • pn 的因子,并且 p2 也是 n 的因子,那么 ψ(n)=ψ(n/p)(p1)

那么,如果用上 d|nϕ(d)=n 这条结论,可以得出 ψ(n)=nd|n,dnψ(d)。这样也可以在 O(nlogn) 的复杂度来求欧拉函数,并且代码更好记也更好写。

除此以外,由于 105 以内的数字最多有 128 个因数,因此可以先遍历存下所有数字的因数。总体时间复杂度为 O(mlogm)

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
vector<int> phi;
vector<vector<int>> fac;
vector<int> getPhi(int n) {
vector<int> f(n + 1);
for (int i = 1; i <= n; i++) {
f[i] = i;
}
for (int i = 1; i <= n; i++) {
fac[i].push_back(i);
for (int j = 2 * i; j <= n; j += i) {
f[j] -= f[i];
fac[j].push_back(i);
}
}
return f;
}
vector<int> getPhi1(int n) {
vector<int> f(n + 1);
for (int i = 1; i <= n; i++) {
f[i] = i;
fac[i].push_back(1);
}
for (int i = 2; i <= n; i++) {
if (f[i] == i) {
for (int j = i; j <= n; j += i) {
f[j] = f[j] / i * (i - 1);
}
}
for (int j = i; j <= n; j += i) {
fac[j].push_back(i);
}
}
return f;
}
void solve() {
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
sort(a.begin(), a.end());
unordered_map<int, int> mp;
ll res = 0;
for (int i = 0; i < n; i++) {
for (int x : fac[a[i]]) {
res += 1ll * mp[x] * phi[x] * (n - i - 1);
mp[x]++;
}
}
cout << res << "\n";
}
int main(){
ios::sync_with_stdio(false); cin.tie(nullptr);
int T;
cin >> T;
int n = 1e5;
fac.resize(n + 1);
phi = getPhi(n);
while (T--) {
solve();
}
}

本文作者:kpole

本文链接:https://www.cnblogs.com/1625--H/p/17860768.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   kpole  阅读(52)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起