【题解】[CF888G] Xor-MST
在博客园的第一篇文章!
[CF888G]Xor-MST 解题报告
目录
题意、思路、代码实现、参考资料
题意
给定有个节点的无向完全图,每个点有点权, 节点与节点之间的边权为 . 计算该图最小生成树的权值.
思路
处理异或:Trie
最小生成树:
已知:
我们会求n个数中任取两数的最大、最小异或和,由此算是处理了有关的事;
对三种我们已知的MST算法进行选择:
- Kruskal
贪心,从小到大将边加入当前选出的、用于构成MST的边的集合。需要维护若干集合,会用到并查集。 - Prim
从点出发,选择与当前节点距离最小的(并且不在集合里的)点加入集合,并连上那条边。 - Borůvka (Sollin)
遍历没用过的边,寻找每个连通块的最小出边,将这条边两个端点所在的两个连通块合并。也需要用到并查集。通俗言之,相当于将连通块当成点来跑Kruskal,或者说,用“点”带“连通块”的形式跑Prim。 由于只需要 log N次合并,在处理边权与点权有关/一些(???)的问题时会有优势。
此题就是用Borůvka的典例。
LG @Tweetuzkidalao的这篇题解写得很清楚,在此感谢这位神犇。
整理一下,我们找到了思路:Borůvka + 01 Trie!o( ̄▽ ̄)ブ
但是在代码实现中,一个小问题出现了。
代码实现
怎么找一个连通块的最小出边呢?
由常识可知,当两个数在01 Trie里的LCA最深时,这两个数的异或值最小。
证明:,显然左式=右式+1.
所以权值在01 Trie上LCA最深的两个点必然先连通。因此,采用分治思想,字典树上当前节点的0儿子和1儿子联通的代价为 在0儿子里的点的MST权值+1儿子里的点的MST权值+0块和1块之间最小边的边权。最小边的边权即为在0儿子和1儿子里分别找一个点,将这两个点的权异或得到的最小值。
AC Code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=2e5+5;
const ll INF=2147483647;
vector<int> a;
int trie[N<<5][2],cnt;
int n;
void ins(int x){
int u=0;
for(int i=31;i>=0;i--){
if(!trie[u][x>>i &1]) trie[u][x>>i &1]=++cnt;
u=trie[u][x>>i &1];
}
}
ll xr(int t,int f,int r){
if(t==-1) return 0;
int u=r;
ll ret=0;
for(int i=t;i>=0;i--){
if(trie[u][f>>i &1]){
u=trie[u][f>>i &1];
} else {
u=trie[u][!(f>>i &1)];
ret+=1<<i;
}
}
return ret;
}
ll b(const vector<int> &now,int t,int r){//boruvka,t为当前递归到的层数
if(t<0 || !r) return 0;
int l=now.size();
vector<int>ch[2];// ch向量用于存当前分治树的0儿子和1儿子下的点的权值
for(int i=0;i<l;i++){
ch[now[i]>>t &1].push_back(now[i]);
}
if(!ch[0].size()) return b(ch[1],t-1,trie[r][1]);
if(!ch[1].size()) return b(ch[0],t-1,trie[r][0]);
ll minn=INF;
int siz=ch[0].size();
for(int i=0;i<siz;i++){
minn=min(minn,(1<<t)*1LL+xr(t-1,ch[0][i],trie[r][1]));
//取1儿子里的点与0儿子里的点分别异或,取最小值作为当前两个连通块联通的代价
}
return minn+b(ch[0],t-1,trie[r][0])+b(ch[1],t-1,trie[r][1]);
}
int main(){
ins(0);
scanf("%d",&n);
for(int i=0,j;i<n;i++){
scanf("%d",&j);
a.push_back(j);
ins(j);
}
printf("%lld",b(a,30,1));
return 0;
}
一点叭叭:花了两天晚上。第一天晚上特意找最小生成树的模板题打了Borůvka,第二天晚上花了两个半小时看题解和写代码和看题解。实际耗时远不及此。
参考资料
- 洛谷 @Tweetuzkidalao的这篇题解
- OI WIKI
补充:更多关于的事情
- (可以用来求树上最长异或路径)
- 它只改变后 较小的数 位。这可以大幅减少枚举工作。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现