题目
给定n个点,第i个点的权值为p[i],任意两点x,y都有边,边权为 min(px%py,py%px) 。问n个点的最小生成树的大小。
数据范围
对于30%的数据,保证1<=n<=10^3。
对于另外40%的数据,保证1<=p_i<=10^6。
对于100%的数据,保证1<=n<=10^5,1<=p_i<=10^7。
时间限制7s,空间限制768MB。
题解
条件
①每条边的边权为
min(px%py,py%px)
。
②总共要选n-1条边构造mst。
困惑点
①不知道究竟选哪条边作为mst(选的边没有标准)选的边有可能都很小且属于同个连通块。
②
min(px%py,py%px)
并不是一个确定的值,它依
px
和
py
而定。
突破口
①有一个显然的结论,如果p相等,可以缩成一个点。(当时为什么没有去打?因为觉得那些边可能会属于不同的连通块,然而这是不可能的,设有c个点的p相等,不管怎么样都会选出c-1条权值为0的边连接这个连通块)听说这样可以多拿30分!!!!
②我们想去掉边权中的
min
,可以发现边权一定为
px%py
,如果
px>py
。但我一直在想为什么
px%py<py=py%px
我蠢啊!!这不是显然吗!!!
这样就去掉min了。
③根据②,为了方便去掉min,统一设
x<y
时,
px<py
。所以将p从小到大排序。
主要思路
如果实在不会做,也可以猜一下结论:设点i为
pi
,对于每一个>1的整数x,找
≥x∗pi
的最小的
pj
,这些边
(i,j)
一定能构成mst。
这样就AC了?为什么!!!
这个在赛场上怎么证?正着证似乎不好证,那就用反证法。
假设有一条边
(a,b)
不满足条件,那么必定存在
x∗pa≤pb<(x+1)∗pa
,
那么存在一系列点
c1,c2,...,ck
使得
x∗pa≤pc1<pc2<...<pck<pb
。
那么根据题意我们一定会选择
(a,c1),(c1,c2),...,(ck−1,ck),(ck,b)
。选这些边的总代价恰好为
pb%a
。选择这些边组成了一条路径,这个路径可能存在环,从而利用环切性质删掉一条边,所以这样的生成树可能比它优,但一定不会比他劣。
具体做法
由于n,p很大,怎么快速地知道最靠近
x∗pi
的数是哪一个呢?用一个桶,
t[i]
表示最靠近i的p值。从后往前做一遍就好了。
边数很大且P相对于时间复杂度很小,用桶排好过快排。
总结
①对于mst问题,最难的点就是选择mst的边没有标准。这样子的解决办法大概有:
⑴将问题分成若干个子问题,每个子问题求出一种最优解,再合并。
⑵将边的范围缩小,然后尝试证明。当然,比赛时没有头绪的情况下可以凭借这个赌一把(本题就用这个方法)。
②如果权值带式子(带式子的东西最麻烦),能够化简或者消掉什么max,min之类的就消掉。如果像
px<py
就能将什么max,min消掉,直接对p进行排序,编号小的p值小,方便化简权值。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 100010
#define LL long long
#define lim 10000010
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
struct note{
int p,x,y;
};note a[N*400];
int i,j,k,l,n,m,cnt,x,mx;
int tot,gx,gy;
int s[N],o[lim];
int f[N];
int t[lim],t1[lim],t2[lim];
int odr[N*400];
LL ans;
int read(){
int fh=1,res=0;char ch;
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')fh=-1,ch=getchar();
while(ch>='0'&&ch<='9')res=res*10+ch-'0',ch=getchar();
return res*fh;
}
int get(int x){
return x==f[x]?x:f[x]=get(f[x]);
}
int main(){
n=read();
fo(i,1,n)s[i]=read(),mx=max(mx,s[i]);
sort(s+1,s+n+1);
cnt=unique(s+1,s+n+1)-s-1;n=cnt;
fo(i,1,n)t[s[i]]=s[i],o[s[i]]=i;
fd(i,lim-2,0)if(!t[i]){
t[i]=t[i+1];
o[i]=o[i+1];
}
fo(i,1,n-1){
a[++tot].x=i;a[tot].y=o[s[i+1]];a[tot].p=s[i+1]%s[i];
j=s[i];
while(j<lim-10){
if(!t[j])break;
if(!t[j+s[i]])o[j+s[i]]=n,t[j+s[i]]=mx;
if(j<=lim && t[j]!=t[j+s[i]]){
a[++tot].x=i;
a[tot].y=o[t[j+s[i]]];
a[tot].p=s[o[t[j+s[i]]]]%s[i];
}
j+=s[i];
}
}
memset(t1,0,sizeof(t1));
fo(i,1,tot)t1[a[i].p]++;
fo(i,1,mx)t1[i]+=t1[i-1];
fo(i,1,tot)odr[t1[a[i].p]--]=i;
fo(i,1,n)f[i]=i;cnt=0;
fo(i,1,tot){
gx=get(a[odr[i]].x);
gy=get(a[odr[i]].y);
if(gx!=gy){
ans=ans+(LL)a[odr[i]].p;
f[gy]=gx;
cnt++;
if(cnt==n-1)break;
}
}
printf("%lld",ans);
return 0;
}