(CJOJ)P2634 - NOIP2017模拟失格
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了(CJOJ)P2634 - NOIP2017模拟失格相关的知识,希望对你有一定的参考价值。
传送门:http://oj.changjun.com.cn/problem/detail/pid/2634
题目大意:给你n个点,每一个点都有一个权值。一条边的边权为min(Vx%Vy,Vy%Vx)。问你怎样构造一棵树,使得边权之和最小。
题解:
首先对特殊条件发掘性质:min(Vx%Vy,Vy%Vx)。不妨设Vx>Vy,那么Vy%Vx=Vy,而Vx%Vy<Vy。故边权就是Vx%Vy。
然后我们想怎样连边,我们当然是要想所连的边尽量的小而不是连成一个团!所以对于权值x来说,在x~2x范围之内,当然是越小越好,那么我们就直接枚举x的倍数,然后二分查找范围内最小值即可。
这样再跑一次克鲁斯卡尔即可。
复杂度分析:
1.连边,O(n*logn*logn)
2.排序,O(n*logn*logn)
3.克鲁斯卡尔,O(n*logn*logn)
所以总复杂度还是O(n*logn*logn)。正好卡过。
1 /*对于一个点枚举倍数,向刚刚≥倍数的连边即可,注意1倍的时候不能等于。然后克鲁斯卡尔即可。复杂度O(n*logn*logn)*/ 2 #include<cstdio> 3 #include<iostream> 4 #include<algorithm> 5 using namespace std; 6 #define RG register 7 #define LL long long 8 const int MAXN=1e5+7,MAXM=1e7+7; 9 int n,Max,sz,c,Left,tp; 10 int p[MAXN],fa[MAXN],cnt[MAXM]; 11 LL ans; 12 struct ed 13 { 14 int f,t,l; 15 }edge[MAXN*30]; 16 bool comp(ed x,ed y) { return x.l<y.l; } 17 int find(RG int x) 18 { 19 if(x!=fa[x])fa[x]=find(fa[x]); 20 return fa[x]; 21 } 22 int search(RG int v,RG int gg) 23 { 24 RG int L=1,R=sz,mid,res=R+1; 25 while(L<=R) 26 { 27 mid=(L+R)/2; 28 if(v<p[mid] || (mid!=gg&&v==p[mid])) 29 res=mid,R=mid-1; 30 else 31 L=mid+1; 32 } 33 return res; 34 } 35 int main() 36 { 37 scanf("%d",&n); 38 for(int i=1,x;i<=n;i++) 39 { 40 scanf("%d",&x); 41 cnt[x]++; 42 if(Max<x) Max=x; 43 } 44 for(int i=1;i<=Max;i++)if(cnt[i])p[++sz]=i,fa[sz]=sz; 45 Left=sz-1; 46 for(RG int i=1;i<=Max;i++) 47 if(cnt[i]) 48 { 49 c++; 50 for(RG int j=i,k;j<=Max;j+=i) 51 { 52 k=search(j,c); 53 if(k<=sz) 54 { 55 edge[++tp].f=c; 56 edge[tp].t=k; 57 edge[tp].l=p[k]%j; 58 } 59 } 60 } 61 sort(edge+1,edge+tp+1,comp); 62 for(RG int i=1,x,y;i<=tp;i++) 63 { 64 x=edge[i].f,y=edge[i].t; 65 if(find(x)!=find(y)) 66 { 67 ans+=edge[i].l; 68 fa[find(x)]=find(y); 69 Left--; 70 } 71 if(Left<=0) break; 72 } 73 printf("%lld\n",ans); 74 return 0; 75 }
以上是关于(CJOJ)P2634 - NOIP2017模拟失格的主要内容,如果未能解决你的问题,请参考以下文章