codeforces 888G Xor-MST

You are given a complete undirected graph with n vertices. A number ai is assigned to each vertex, and the weight of an edge between vertices i and j is equal to aixoraj.

Calculate the weight of the minimum spanning tree in this graph.

题目大意:

边权为两端点点权的异或,求一个完全图的最小生成树

Input

The first line contains n (1 ≤ n ≤ 200000) — the number of vertices in the graph.

The second line contains n integers a1, a2, ..., an (0 ≤ ai < 230) — the numbers assigned to the vertices.

Output

Print one number — the weight of the minimum spanning tree in the graph.

Examples
Input
Copy
5
1 2 3 4 5
Output
8
Input
Copy
4
1 2 3 4
Output
8

 本题要用到一种求生成树的方法Boruvka

给所有单词维护一颗trie树

按照这种求生成树的方法,我们每一次要在trie中去掉该集合的点,再求最小的边

这样很麻烦,实际上可以在trie树内部合并

首先左右子树中的所有点显然是已经在一个连通块内的。我们只需要在左右子树的联通块中各选出一
个点,连边即可

这样如果左子树存在右子树合并肯定最优,因为他们公共前缀最长

考虑启发式合并当前点的左右子树

枚举关键点数更小的子树的关键点,带入另一个子树求出最小边权

最后在根合并成一颗生成树

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 #include<algorithm>
 6 #include<vector>
 7 using namespace std;
 8 typedef long long lol;
 9 vector<lol>Q[4000001];
10 int ch[4000001][2],pos,dep[4000001],n;
11 lol pw[31],ans,a[200001];
12 void insert(lol x)
13 {int i;
14   int now=0;
15   Q[now].push_back(x);
16   for (i=30;i>=0;i--)
17     {
18       int flag=(bool)((x>>i)&1);
19       if (ch[now][flag]==0) ch[now][flag]=++pos;
20       now=ch[now][flag];
21       x-=flag*pw[i];
22       Q[now].push_back(x);dep[now]=i;
23     }
24 }
25 lol merge(int x,lol t)
26 {int i;
27   lol as=0;
28   for (i=dep[x]-1;i>=0;i--)
29     {
30       int flag=(bool)((t>>i)&1);
31       if (ch[x][flag]) x=ch[x][flag];
32       else as+=pw[i],x=ch[x][!flag];
33     }
34   return as;
35 }
36 lol query(int rt)
37 {int i;
38   if (Q[rt].size()==1) return 0;
39   if (ch[rt][0]) ans+=query(ch[rt][0]);
40   if (ch[rt][1]) ans+=query(ch[rt][1]);
41   if (!ch[rt][0]||!ch[rt][1]) return 0;
42   int flag=0;
43   if (Q[ch[rt][0]].size()>Q[ch[rt][1]].size()) flag=1;
44   int sz=Q[ch[rt][flag]].size();
45   lol tmp=2e15;
46   for (i=0;i<sz;i++)
47     tmp=min(tmp,merge(ch[rt][flag^1],Q[ch[rt][flag]][i]));
48   return tmp+pw[dep[rt]-1];
49 }
50 int main()
51 {int i;
52   cin>>n;
53   pw[0]=1;
54   for (i=1;i<=30;i++)
55     pw[i]=pw[i-1]*2;
56   for (i=1;i<=n;i++)
57     {
58       scanf("%lld",&a[i]);
59     }
60   sort(a+1,a+n+1);
61   n=unique(a+1,a+n+1)-a-1;
62   for (i=1;i<=n;i++)
63     {
64       insert(a[i]);
65     }
66   ans+=query(0);
67   printf("%lld\n",ans);
68 }

 

首先左右子树中的所有点显然是已经在一个连通块内的。我们只需要在左右子树的联通块中各选出一
个点,连边即可

posted @ 2018-03-27 20:13  Z-Y-Y-S  阅读(587)  评论(0编辑  收藏  举报