Description

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

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

Solution

可以先把所有p相等的连起来,显然费用为0

对于一个点,考虑它连出去的边。
枚举Pi的倍数,找到离它右边最近的一个连起来。

正确性。。比较显然吧。

最多可以连出MaxP log P条边

然后直接Kruskal
随便去掉一些重复的应该就能够通过

排序的话用桶排也是可以的

毕竟Time limits 7s 还开O2

Code

#pragma GCC optimize(2)
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define N 100005
#define M 10000005
#define LL long long
using namespace std;
int c[N],n,m,dis[N],f[N],m1,m2;
int lt[M],lt1[M];
LL ans;
bool bz[N];
struct node
{
int x,y,c;
friend bool operator <(node x,node y)
{
return x.c<y.c;
}
}a[5*M];
int getf(int k)
{
if(f[k]==k||f[k]==0) return k;
return f[k]=getf(f[k]);
}
int main()
{
cin>>n;
int mx=0;
fo(i,1,n) scanf("%d",&c[i]),mx=max(mx,c[i]);
sort(c+1,c+n+1);
m1=n-1;
fo(i,1,n)
{
if(c[i]!=c[i-1]) lt[c[i]]=i,lt1[c[i]-1]=i;
if(c[i]==c[i+1]) m1--,f[getf(i+1)]=getf(i);
}
fod(i,mx,1)
{
if(lt[i]==0) lt[i]=lt[i+1];
if(lt1[i]==0) lt1[i]=lt1[i+1];
}
fo(i,1,n)
{
if(c[i]!=c[i-1])
{
for(int j=1;c[i]*j<=mx;j++)
{
if(j>1&<[c[i]*j]==lt[c[i]*(j-1)]) continue;
int y=lt[c[i]*j];
if(j==1) y=lt1[c[i]*j];
if(y==0) continue;
a[++m].c=c[y]%c[i];
a[m].y=y,a[m].x=i;
}
}
}
sort(a+1,a+m+1);
LL ans=0;
for(int i=1;i<=m&&m1;i++)
{
int fx=getf(a[i].x),fy=getf(a[i].y);
if(fx!=fy) ans+=a[i].c,f[fx]=fy,m1--;
}
printf("%lld",ans);
}