CF888G Xor-MST
题意:
- 给定 \(n\) 个结点的无向完全图。每个点有一个点权为 \(a_i\)。连接 \(i\) 号结点和 \(j\) 号结点的边的边权为 \(a_i\oplus a_j\)。
- 求这个图的
最小生成树
的权值。 - \(1\le n\le 2\times 10^5\),\(0\le a_i< 2^{30}\)。
思路:
看到异或,想到 01trie
和 线性基
线性基明显不大行,考虑 01trie
先将每个节点的权值插入 \(trie\) 中,如下图
容易发现,若是两个点连边,权值就是 \(lca\) 以下的边的异或值
考虑深度最大的 \(lca\),可以发现,若是 \(a_i\) 两两不同,那么有且仅有 \(n-1\) 个这样的 \(lca\)
最好的结果就是将这 \(n-1\) 个 \(lca\) 以下的边的值贪心的算出来
贪心:尽量让高位的 \(1/0\) 相同,使得两个值 \(xor\) 的结果尽量小
\(dfs\) 一遍找到所有有两个儿子的节点搞贪心即可
#include<bits/stdc++.h>
using namespace std;
const int inf=1<<30;
const int N=6e6+5;
#define ll long long
int n,tot;
int t[N][2];
ll ans;
inline void insert(int x){
int rt=0;
for(int i=30;i>=0;--i){
int c=(1<<i)&x?1:0;
if(!t[rt][c]) t[rt][c]=++tot;
rt=t[rt][c];
}
}
inline ll get_mn(int l,int r,int dep){
if(dep<0) return 0;
ll a1=inf,a2=inf;
if(t[l][0]&&t[r][0]) a1=get_mn(t[l][0],t[r][0],dep-1);
if(t[l][1]&&t[r][1]) a2=get_mn(t[l][1],t[r][1],dep-1);
if(a1!=inf||a2!=inf) return min(a1,a2);
if(t[l][0]&&t[r][1]) a1=get_mn(t[l][0],t[r][1],dep-1)+(1<<dep);
if(t[l][1]&&t[r][0]) a2=get_mn(t[l][1],t[r][0],dep-1)+(1<<dep);
return min(a1,a2);
}
inline void dfs(int rt,int dep){
if(dep<0) return;
if(t[rt][0]&&t[rt][1]) ans+=get_mn(t[rt][0],t[rt][1],dep-1)+(1<<dep);
if(t[rt][0]) dfs(t[rt][0],dep-1);
if(t[rt][1]) dfs(t[rt][1],dep-1);
}
signed main(){
ios::sync_with_stdio(0);
cin>>n;
for(int i=1,x;i<=n;++i){
cin>>x;
insert(x);
}
dfs(0,30);
cout<<ans<<endl;
}
标签:图论,le,int,lca,练习,结点,权值,贪心 来源: https://www.cnblogs.com/into-qwq/p/16450135.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。