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