Trie树练习题
Trie树练习题
T1 「一本通 2.3 例 2」The XOR Largest Pair
在给定的
数据范围
对于 100%的数据,
1.朴素算法并用二分查去匹配二进制下每个数最高位的0还是能卡过的
2.结合异或的运算规则,怎样才能使两个数的异或和尽量大呢?答案是尽量让二进制下每一位都不同,如果对于每个数x,能直接找到某一位和它不同的数,那就可以贪心选取二进制下尽量不一样的数了。那怎么找呢?
3.结合数据范围发现每个数写成二进制最多才31位,每一位01两个状态。所以对每个数的二进制建trie树(深度越浅代表的二进制位越高,树的size最大为
- 为什么这样贪一定使对的呢?假设当前数的第k位是0,trie树上的第k位存在1,那要么选这个1,则对结果有
的贡献;要么不选1选0,则后面的数位总共对结果最多有 的贡献,故第k位选相反数一定使更优的。算法正确性得证。
#include<bits/stdc++.h>
using namespace std;
#define F(i,l,r) for(int i=l;i<=r;++i)
#define G(i,r,l) for(int i=r;i>=l;--i)
const int N=1e5+5,M=20;
int n,num=0,mx=-1,z,a[N*M],ch[N*M][2],val[N*M];
inline void insert(int x){
int p=0;
G(i,30,0){
z=x&(1<<i)?1:0;
if(!ch[p][z]) ch[p][z]=++num;
p=ch[p][z];
} val[p]=x;
}
inline void qry(int x){
int p=0;
G(i,30,0){
z=x&(1<<i)?1:0;
if(ch[p][1^z]) p=ch[p][1^z];
else if(ch[p][z]) p=ch[p][z];
}
mx=max(mx,x^val[p]);
}
int main(){
scanf("%d",&n);
F(i,1,n) scanf("%d",&a[i]),insert(a[i]);
F(i,1,n) qry(a[i]);
printf("%d",mx);
return 0;
}
T2「一本通 2.3 练习 5」The XOR-longest Path
给定一棵
- 写在前面:审题一定要审清楚!没有指定
是根,“路径”的意思也不是说一定路径从根节点出发
(不然直接dfs这题不就秒了么)
1.暴力:分别以每个点为根跑根到每个点的异或和,最后取max,时间复杂度
2.Trie树优化:第一感觉一定是这道题和上面那道题很像,但关键是上题只需两两匹配,容易构建trie树,这道题要选很多条边权出来匹配,想了很久都不知道怎么建trie树。
考虑转化问题
(1)先任选一个点为根比如1,sum[i]表示根到所有点的路径异或和,这个可以一次dfs搞定
(2)接下来要利用一个很NB的性质(其实主要是我以前没见过doge):x ^ x=0,自己过程中还发现一个性质,顺带提一下吧虽然用不上:x^y=z z^y=x(有点像加减法)
(3)这个性质有什么用呢?学了LCA都知道树上任意两点的路径可以转化为 x 到 lca 的路径和 lca 的 y 的路径拼起来
(4)如果这道题询问的是路径上边权的代数和最大,那我们肯定求个lca就做完了。那异或和呢?
(5)考虑把sum[x]和sum[y]展开

展开即每一条边权来异或,由于异或有交换律,发现1到p(即lca(x,y))的边权会和自己异或,于是乎全部都变成了0,只剩下x到p,p到y的边权异或和,这恰恰就是我们想要的,甚至不用求lca
所以路径异或和又转换成了sum数组两两之间异或和的最大值,即T1所求。复杂度
END~
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具