1. 程式人生 > >【JZOJ A組】GCD生成樹

【JZOJ A組】GCD生成樹

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;
}