Trie树练习题

Trie树练习题

T1 「一本通 2.3 例 2」The XOR Largest Pair

在给定的 N 个整数A1,A2,...AN中选出两个进行异或运算,得到的结果最大是多少?

数据范围

对于 100%的数据,1N105,0Ai231

1.朴素算法并用二分查去匹配二进制下每个数最高位的0还是能卡过的

2.结合异或的运算规则,怎样才能使两个数的异或和尽量大呢?答案是尽量让二进制下每一位都不同,如果对于每个数x,能直接找到某一位和它不同的数,那就可以贪心选取二进制下尽量不一样的数了。那怎么找呢?

3.结合数据范围发现每个数写成二进制最多才31位,每一位01两个状态。所以对每个数的二进制建trie树(深度越浅代表的二进制位越高,树的size最大为N31 ),对于每个数,在trie树上每走一步就贪心地选当前位和它不一样的数

  • 为什么这样贪一定使对的呢?假设当前数的第k位是0,trie树上的第k位存在1,那要么选这个1,则对结果有2k的贡献;要么不选1选0,则后面的数位总共对结果最多有2k1的贡献,故第k位选相反数一定使更优的。算法正确性得证。

Code:

#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

给定一棵 N 个点的带权树,求树上最长的异或和路径

  • 写在前面:审题一定要审清楚!没有指定1是根,“路径”的意思也不是说一定路径从根节点出发

(不然直接dfs这题不就秒了么)

1.暴力:分别以每个点为根跑根到每个点的异或和,最后取max,时间复杂度O(N2),大概能过40分

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所求。复杂度O(31N)

END~

posted @   superl61  阅读(17)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示