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 ai xor aj.
Calculate the weight of the minimum spanning tree in this graph.
题目大意:
边权为两端点点权的异或,求一个完全图的最小生成树
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.
Print one number — the weight of the minimum spanning tree in the graph.
本题要用到一种求生成树的方法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 }
首先左右子树中的所有点显然是已经在一个连通块内的。我们只需要在左右子树的联通块中各选出一
个点,连边即可