【JZOJ A组】GCD生成树

版权声明:转载者乖乖♂站好 https://blog.csdn.net/Eric1561759334/article/details/82916293

Description
在这里插入图片描述

Input
在这里插入图片描述

Output
在这里插入图片描述

Sample Input
5
5 6 7 10 21

Sample Output
17

Data Constraint
在这里插入图片描述

Hint

在这里插入图片描述

思路

先将权值相等的点消去。

仿照最大生成树算法从大往小枚举边权,假设当前枚举的边权值为 i,这种边显然存在于点权为 ki 的点之间,我们暴力枚举 k,这些点中有些点已经相连(两个点的点权为 k1i 和 k2i且 k1 与 k2 不互质时),而另外的点未相连(两个点的点权为 k1i 和 k2*i 且 k1 与 k2 互质时)。那么我们将所有未相邻的联通块连起来,并计算答案。

枚举倍数的复杂度是调和级数。
复杂度 O(nlogn)。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
const int N=1e5+77;
int a[N],fa[N],t[N],n;
int getfa(int x)
{
	return fa[x]==x?x:fa[x]=getfa(fa[x]);
}
int main()
{
//	freopen("gcd.in","r",stdin);
//	freopen("gcd.out","w",stdout);
	scanf("%d",&n); int mx=0; ll ans=0;
	for(int i=1; i<=n; i++)
	{
		scanf("%d",&a[i]); mx=max(mx,a[i]);
		if (!t[a[i]]) fa[i]=t[a[i]]=i; else ans+=a[i];
	}
//	printf("*\n");
	for(int i=mx; i>0; i--)
	{
		int now=0;
		for(int j=1; i*j<=mx; j++)
		if(t[i*j])
		{
//			printf("i=%d j=%d i*j=%d t[i*j]=%d\n",i,j,i*j,t[i*j]);
			int x=getfa(t[i*j]);
//			printf("*\n");
			if(!now) now=x;
			else
			{
//				printf("#\n");
				if (now==x) continue;
				ans+=i,fa[x]=now;
			}
//			printf("*\n");
		}
	}
	printf("%lld",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Eric1561759334/article/details/82916293