失格

Description

胆小鬼连幸福都会害怕,碰到棉花都会受伤,有时还被幸福所伤。
——太宰治《人间失格》

回顾我的一生,一共有n个事件,每一个事件有一个幸福值pi。
我想用n-1条线把所有的事件连起来,变成一个连通块。一条连接了事件x和事件y的线会产生min(px mod py , py mod px)的喜悦值。
日日重复同样的事,遵循着与昨日相同的惯例,若能避开猛烈的狂喜,自然也不会有悲痛的来袭。因此,我想知道连接起来之后产生喜悦值最小是多少。

solution

比较简单的题,注意到边权是固定的,设点权较大的为 \(i\),较小的为 \(j\),边权即为 \(a[i]\%a[j]\),容易发现 \(a[j]+1\)\(a[j]+2\) 要更优,所以可以忽略掉很多没用的边,对于大于 \(a[j]\) 的每一个倍数取出最小的一个即可,我们可以二分求解,注意到这样是会被卡的,需要unique一遍,因为这样最坏复杂度就变成了 \(\sum{a[n]/i}\),要记住这个复杂度是 \(O(n*In_{n})\)的,边数也是一样,然后跑kruskal即可

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#define RG register
#define il inline
#define iter iterator
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
typedef long long ll;
const int N=100005;
int n,a[N],fa[N];
inline int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}

inline int midit(int l,int x){
	int r=n,mid,ret=-1;
	while(l<=r){
		mid=(l+r)>>1;
		if(a[mid]>=x)ret=mid,r=mid-1;
		else l=mid+1;
	}
	return ret;
}

struct node{
	int x,y,dis;
	node(){}
	node(int _x,int _y,int _dis){x=_x;y=_y;dis=_dis;}
	bool operator <(const node &pr)const{return dis<pr.dis;}
}e[N*20];

void work()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	sort(a+1,a+n+1);
	n=unique(a+1,a+n+1)-a-1;

	int x,y,tot=0,cnt=0;ll ans=0;
	for(int i=1;i<n;i++){
		fa[i]=i;
		for(int j=a[i];j<=a[n];j+=a[i]){
			x=midit(i+1,j);
			if(x==-1)break;
			e[++tot]=node(i,x,a[x]%a[i]);
		}
	}
	fa[n]=n;
	sort(e+1,e+tot+1);
	for(int i=1;i<=tot;i++){
		x=e[i].x;y=e[i].y;
		if(find(x)==find(y))continue;
		fa[find(y)]=find(x);
		ans+=e[i].dis;
		cnt++;
		if(cnt==n-1)break;
	}
	printf("%lld\n",ans);
}

int main()
{
	work();
	return 0;
}

posted @ 2017-11-09 09:46  PIPIBoss  阅读(221)  评论(0编辑  收藏  举报