失格

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
",ans);
}

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

原文地址:https://www.cnblogs.com/Yuzao/p/7807929.html