【问题描述】
胆小鬼连幸福都会害怕,碰到棉花都会受伤,有时还被幸福所伤。
——太宰治《人间失格》
回顾我的一生,一共有 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。

失格_单调栈

失格_乱搞_02

 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 }