给定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
#include
#include
#include
#include
#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(j10){
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;
}