CF888 G Xor-MST
CF888 G Xor-MST
1 杂言
一看到MST,立即想到kruskal,立即想到暴力跑边,立即想到TLE,立即放弃了这道题。蒟蒻的想象惟在这一层能够如此跃进
2 题解
首先,看到异或,就要用trie
接下来考虑一个(比较冷)的最小生成树算法:Borůvka算法
先把所有节点的权值加入01trie
对于任意一个非叶子节点(p) ,它的0子树和1子树一定没有联通
大致证明是,考虑连接这0子树和1子树的花费至少为(2^a) ,而一边若还有没有联通的块,花费必然小于(2^a) ,因此会先连接这两块((a) 为(p) 的深度)
那么,在两个子树分别联通之后,我们只需要找到最小代价联通两个子树的边即可,这部分用trie解决
接下来,两个子树分别联通的代价可以用同样的方法分别计算
也就是说,这个trie是B算法的分治树
按这个思路分治下去即可
3 code
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <vector> #define int long long using namespace std; const int N=200010; inline void read(int &x) { x=0; int f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if (ch=='-') { f=-1; } ch=getchar(); } while(ch>='0'&&ch<='9') { x=x*10+ch-'0'; ch=getchar(); } x*=f; } vector<int> a; struct note { int ch[2]; }; note t[N<<5]; int siz; inline void insert(int &o,int val,int p) { if (!o) { o=++siz; } if (p==-1) { return; } int k=(val>>p)&1; insert(t[o].ch[k],val,p-1); } int query(int o,int val,int p) { if (p==-1) { return 0; } int k=(val>>p)&1; return (t[o].ch[k]?query(t[o].ch[k],val,p-1):query(t[o].ch[!k],val,p-1)^(1<<p)); } int solve(vector<int> v,int o,int p) { if (!o) { return 0; } int len=v.size(); vector<int> ve[2]; for(int i=0;i<len;i++){ ve[(v[i]>>p)&1].push_back(v[i]); } if (!ve[0].size()) { return solve(ve[1],t[o].ch[1],p-1); } if (!ve[1].size()) { return solve(ve[0],t[o].ch[0],p-1); } len=ve[1].size(); int cst=2147483647; for(int i=0;i<len;i++) { cst=min(cst,(1<<p)+query(t[o].ch[0],ve[1][i],p-1)); } return cst+solve(ve[1],t[o].ch[1],p-1)+solve(ve[0],t[o].ch[0],p-1); } int n; int rt; signed main() { read(n); rt=0; for(int i=1;i<=n;i++) { int x; read(x); a.push_back(x); insert(rt,x,30); } printf("%lld ",solve(a,rt,30)); return 0; }
Created: 2021-01-22 周五 15:56