【问题描述】
胆小鬼连幸福都会害怕,碰到棉花都会受伤,有时还被幸福所伤。
——太宰治《人间失格》
回顾我的一生,一共有 n 个事件,每一个事件有一个幸福值 p_i。
我想用 n-1 条线把所有的事件连起来,变成一个连通块。一条连接了事件 x
和事件 y 的线会产生 min(p_x mod p_y,p_y mod p_x)的喜悦值。
日日重复同样的事,遵循着与昨日相同的惯例,若能避开猛烈的狂喜,自然
也不会有悲痛的来袭。因此,我想知道连接起来之后产生喜悦值最小是多少。
【输入格式】
文件第一行有一个正整数 n。
接下来 n 行,每行一个正整数 p_i。
【输出格式】
输出只有一行,表示最小的喜悦值。
【样例输入输出 1】
autosadism.in
4
2
6
3
11
autosadism.out
1
【样例输入输出 2】
autosadism.in
4
1
2
3
4
autosadism.out
0
【样例输入输出 3】
autosadism.in
3
4
9
15
autosadism.out
4
【数据范围】
对于 30%的数据,保证 1<=n<=10^3。
对于另外 40%的数据,保证 1<=p_i<=10^6。
对于 100%的数据,保证 1<=n<=10^5,1<=p_i<=10^7。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<vector> 6 using namespace std; 7 struct Node 8 { 9 int u,v; 10 }edge[22000001]; 11 int n,num,p[100001]; 12 bool vis[10000001]; 13 int maxp,Nxt[10000002],set[100001],cnt,dis[22000001]; 14 long long ans; 15 vector<Node>E[10000001]; 16 int find(int x) 17 { 18 if (set[x]!=x) set[x]=find(set[x]); 19 return set[x]; 20 } 21 void add(int u,int v,int dis) 22 { 23 E[dis].push_back((Node){u,v}); 24 } 25 int main() 26 {int i,j,last; 27 cin>>n; 28 for (i=1;i<=n;i++) 29 { 30 scanf("%d",&p[i]); 31 if (vis[p[i]]) i--,n--; 32 else vis[p[i]]=1; 33 maxp=max(maxp,p[i]); 34 } 35 sort(p+1,p+n+1); 36 int now=n; 37 for (i=maxp;i>=1;i--) 38 { 39 if (now>0&&p[now-1]>=i) now--; 40 Nxt[i]=now; 41 } 42 //for (i=1;i<=maxp;i++) 43 //cout<<Nxt[i]<<endl; 44 for (i=1;i<n;i++) 45 { 46 int v=p[Nxt[p[i]+1]]%p[i]; 47 add(i,Nxt[p[i]+1],v); 48 int P=2*p[i]; 49 last=Nxt[p[i]+1]; 50 while (P<=maxp) 51 { 52 int v=Nxt[P]; 53 if (v==0) break; 54 if (last!=v) 55 { 56 if (v) 57 {add(i,v,p[v]%p[i]);} 58 last=v; 59 } 60 P+=p[i]; 61 } 62 } 63 for (i=0;i<=maxp;i++) 64 { 65 for (j=0;j<E[i].size();j++) 66 { 67 ++num; 68 edge[num]=E[i][j]; 69 dis[num]=i; 70 } 71 } 72 for (i=1;i<=n;i++) 73 set[i]=i; 74 for (i=1;i<=num;i++) 75 { 76 int u=find(edge[i].u); 77 int v=find(edge[i].v); 78 if (u!=v) 79 { 80 set[v]=u; 81 ans+=dis[i]; 82 cnt++; 83 if (cnt==n-1) break; 84 } 85 } 86 cout<<ans; 87 }