复习时食用,会比较简略。
原理就不讲了,还不会字典树的先下车吧。
目录
#10049. 「一本通 2.3 例 1」Phone List
#10050. 「一本通 2.3 例 2」The XOR Largest Pair
#10051. 「一本通 2.3 例 3」Nikitosh 和异或
#10052. 「一本通 2.3 练习 1」Immediate Decodability
#10054. 「一本通 2.3 练习 3」Secret Message 秘密信息
#10056. 「一本通 2.3 练习 5」The XOR-longest Path
#10049. 「一本通 2.3 例 1」Phone List
#10050. 「一本通 2.3 例 2」The XOR Largest Pair
题目大意
在给定的N个整数中选出两个进行异或运算。
得到的结果最大是多少?
对于100%的数据,1<=n<=10^5,0<=ai<2^31。
题目分析
百度科普:异或即两个输入相同时为0,不同则为1。
首首先化成二进制建树。
为了让异或值max当然是要01反着跑,即一边跑向1另一边跑向0。
化为二进制后第i位不同则:ans+=2^(i-1)。
每插入一个数就跑一遍,要么你建完树再跑也行。
建树从高位到低位。废话肯定先捡大便宜再捡小便宜。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,len=0;
long long a[100010],ans=0,biao=1;
struct node{int son[10];}tr[3100010];
void bt(long long aa)
{
int now=0,x,d=0,k[40];
long long kkk=biao,aans=0;
for(int i=1;i<=32;i++)
{
if(aa>=kkk) x=1,aa-=kkk;
else x=0;
if(tr[now].son[x]==0)
{
len++,tr[now].son[x]=len,now=len;
tr[now].son[0]=tr[now].son[1]=0;
}
else now=tr[now].son[x];
d++; k[d]=x;//k存这个数化为二进制每一位上的数
kkk/=2;
}
now=0; kkk=biao;
for(int i=1;i<=32;i++)
{
//是1往0跑,是0往1跑
if(tr[now].son[1-k[i]]!=0)
{
aans+=kkk;
now=tr[now].son[1-k[i]];
}
else now=tr[now].son[k[i]];//条件不允许你乱跑
kkk/=2;
}
ans=max(ans,aans);
}
int main()
{
scanf("%d",&n);
tr[0].son[0]=tr[0].son[1]=0;
for(int i=1;i<=31;i++) biao*=2;
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
bt(a[i]);
}
printf("%lld",ans);
return 0;
}
#10051. 「一本通 2.3 例 3」Nikitosh 和异或
题目大意
给定一个含N个元素的数组A,下标从1开始。
找出式子的最大值:(A[l_1]⨁A[l_1+1]⨁…⨁A[r_1])+(A[l_2]⨁A[l_2+1]⨁…⨁A[r_2])。
其中1≤l_1≤r_1<l_2≤r_2≤N,x⨁y表示x和y的按位异或。
对于100%的数据,2<=n<=4*10^5,0<=ai<=10^9。
题目分析
首先记录异或前缀和s[i]=a[1]⊕a[2]⊕a[3]...⊕a[i]。
设l[i]为以i结尾的区间中,异或值的最大值。
因为异或有x⊕x=0的性质,所以区间[l,r]的异或值
=a[l]⊕a[l+1]⊕...⊕a