图论练习
作者:互联网
目录
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