失格
来自 Czy 大佬的题目
Description
给出
n
个点,每一个点有一个点权
Data Constraint
1
<=
Solution
这一题的空间限制很大,时间限制也很大,这提示我们要使用一些有奇迹影响的方法。
一个显然的结论,连接(
i
,
显然
p
相同的点可以直接缩成一个点。
考虑暴力怎么做。
将
但这样会超时,其实这种做法中会枚举很多冗余的边。
先对
p
序列排序。
对于一个
对于它的一个倍数
x
易证,这样连出来的边跑出来的最小生成树与全部边跑出来的最小生成树是一致的。
根据调和级数可以算出,这样连出来的边只有最多
n
调用
Code
#include<iostream>
#include<cstdio>
#pragma GCC optimize(3)
#include<cstring>
#include<algorithm>
#define fo(i,j,l) for(int i=j;i<=l;i++)
#define fd(i,j,l) for(int i=j;i>=l;i--)
using namespace std;
typedef long long ll;
const ll N=22e4,M=32e6,zd=1e7+11;
int p[N],fa[N],la[zd],ne[M],lb[M],bl[M];
int n,m,j,k,l,i,o,P,oo;
void llb(int x,int y,int z)
{
ne[++oo]=la[z]; la[z]=oo; lb[oo]=x; bl[oo]=y;
}
int getfa(int o)
{
if(fa[o]==o)return o;
fa[o]=getfa(fa[o]); return fa[o];
}
int main()
{
cin>>n;
fo(i,1,n)scanf("%d",&p[i]);
sort(p+1,p+n+1);
int o=0; p[0]=0;
fo(i,1,n)
if(p[i]!=p[i-1])p[++o]=p[i]; n=o;
P=p[n];
fo(i,1,n){
int last=0;
fo(l,1,P/p[i]){
int kk=l*p[i];
int le=i,ri=n;
while(le+1<ri){
int mid=(le+ri)/2;
if(p[mid]>=kk)ri=mid;
else le=mid;
}
if(ri!=last)llb(i,ri,p[ri]%p[i]),last=ri;
}
}
fo(i,1,n)fa[i]=i;
ll ans=0; int k=1;
fo(i,0,P)
for(int y=la[i];y;y=ne[y])
if(getfa(lb[y])!=getfa(bl[y])){
fa[fa[lb[y]]]=fa[bl[y]];
ans+=i;
k+=1;
if(k==n){
printf("%lld",ans);
return 0;
}
}
}