登录—专业IT笔试面试备考平台_牛客网
题目大意:给出一个n个数的数组,问有多少个三元组(a[i],a[j],a[k])满足lcm(a[i],a[j])<=gcd(a[j],a[k]),只要两个三元组中任意两个数不同,就算做两个不同的三元组
3<=n<=2e5;1<=a[i]<=2e5
思路:因为两个数的最大公因数一定小于等于两个数中最小的那个数,两个数的最小公倍数一定大于等于两个数中最大的那个数,所以要让lcm(a,b)<=gcd(b,c),唯一成立的条件就是lcm=gcd=b。
因为lcm(a,b)=b,所以a是b 的因数,因为gcd(b,c)=b,所以c是b的倍数,那么我们如果知道b有多少个因数在数组中,记为cnt1[b],有多少个倍数在数组中,记为cnt2[b],那么以b作为三元组中间的那个数时就有cnt1[b]*cnt2[b]个合法三元组。
那么怎么求因数和倍数的数量呢,我们首先记录数组中每个数的出现次数cnt[a[i]],然后遍历数组中所有不重复的数字,对其进行因式分解,如果x是a[i]分解出来的一个因数,且x在数组中,那么cnt1[a[i]]就应该+=cnt[x],cnt2[x]+=cnt[a[i]]。
最后遍历数组,累加cnt1[a[i]]*cnt2[a[i]]的值即可。
//#include<__msvc_all_public_headers.hpp>
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
typedef long long ll;
const ll MOD = 1e9 + 7;
ll n;
int a[N];
ll cnt1[N], cnt2[N];
ll cnt[N];
bool vis[N];
void init()
{for (int i = 1; i <= 200000; i++){cnt[i] = cnt1[i] = cnt2[i] = 0;vis[i] = 0;}
}void solve()
{cin >> n;init();for (int i = 1; i <= n; i++){cin >>a[i];cnt[a[i]]++;//记录数组中每个数的出现次数}for (int i = 1; i <= n; i++){if (vis[a[i]])continue;//只分解第一次出现的数vis[a[i]] = 1;ll temp = a[i];for (int j = 1; j * j <= temp; j++){if (temp % j == 0){if (cnt[j]){//a[i]%j=0求j在数组中,就维护a[i]的因数数量和j的倍数的数量cnt1[j]+=cnt[a[i]];cnt2[a[i]]+=cnt[j];}ll temp2 = temp / j;if (temp2 != j){if (cnt[temp2]){cnt1[temp2] += cnt[a[i]];cnt2[a[i]] += cnt[temp2];}}} }}ll ans = 0;for (int i = 1; i <= n; i++){ans += cnt1[a[i]] * cnt2[a[i]];}cout << ans;cout << '\n';
}
int main()
{ios::sync_with_stdio(false);cin.tie(0);int t;//cin >> t;t=1;while (t--){solve();}return 0;
}