CF1900D - Small GCD【数论超好题】
CF1900D - Small GCD【数论超好题】
前置知识
正题
题意
给定一个数组
思路
由于该题是取三元组中较小的两个数,所以说最大的那个数并不重要,只需要找出所有二元组,并乘上可以为第三个数的数的个数即可。
再而,取三元组并不需要考虑下标,所以我们可以将
显然,我们现在就可以得到一个
sort(a + 1, a + 1 + n);
for (int i = 1; i <= n; i ++) {
for (int j = i + 1; j <= n; j ++) {
ans += gcd(a[i], a[j]) * (n - j);
}
}
那怎么优化呢?
一种优化思路是:仅枚举三元组中一个数,再用每种算法快速求出可能的三元组的值。
那枚举哪个数呢?
中间值:枚举出它之后,最大数的个数已知,且最小数的范围也已知。
那么我们需要快速求出的值就变为了
接着,我们对
所以复杂度瓶颈仅在于
又因为
所以时间复杂度近似于
Code
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
const int N = 8e4 + 10, M = 1e5 + 10;
typedef long long ll;
ll n, t, ans;
ll a[N], pri[M], tot, phi[M], cnt[M];
vector<ll> evn[M];
bool isp[M];
void pre(int Max){//线性筛求欧拉函数
for(int i = 1; i <= Max; i ++) isp[i] = 1;
isp[1] = 0;
phi[1] = 1;
for(int i = 2; i <= Max; i ++){
if(isp[i]){
pri[++ tot] = i;
phi[i] = i - 1;
}
for(int j = 1;j <= tot && i * pri[j] <= Max;j ++){
isp[i * pri[j]] = 0;
if(i % pri[j]) phi[i * pri[j]] = phi[i] * phi[pri[j]];//积性函数的性质
else{
phi[i * pri[j]] = phi[i] * pri[j];
break;
}
}
}
}
signed main() {
ios::sync_with_stdio(0);
for (int i = 1; i <= 1e5; i ++) {//预处理因数
for (int j = 1; j * j <= i; j ++) {
if (i % j == 0) {
evn[i].push_back(j);
if (j * j != i) evn[i].push_back(i / j);
}
}
}
pre(1e5);
for (cin >> t; t; t --) {
cin >> n;
ans = 0;
for (int i = 1; i <= 1e5; i ++) cnt[i] = 0;//多测清空
for (int i = 1; i <= n; i ++) cin >> a[i];
sort(a + 1, a + 1 + n);
for (int i = 1; i <= n; i ++) {
// cntd * phi[d] * (n - i)
for (auto d : evn[a[i]]) ans += cnt[d] * phi[d] * (n - i);//上文推论
for (auto d : evn[a[i]]) cnt[d] ++;//更新 cnt[d]
}
cout << ans << endl;
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】