小苯的三元组 2024年中国传媒大学程序设计大赛

登录—专业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;
}

  • 7
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

timidcatt

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值