Trie树中的最大异或对模板

模板(AcWing 143.最大异或对)

在这里插入图片描述
那么这个题目就是利用Trie树来优化暴力枚举做法,我们对于任意的一个a[i],要让其与另一个数的异或和最大,想到利用贪心的做法,从高位到低位枚举,尽量在树中寻找与a[i]同数位但数值恰好相反的节点。代码如下:

#include<iostream>
using namespace std;
const int N=3e6+5;
int a[N];
int son[N][2],idx;
void insert(int x){
    int p=0;
    for(int i=30;~i;i--){
        int &s=son[p][x>>i&1];
        if(!s) s=++idx;
        p=s;
    }
}
int find(int x){
    int p=0,res=0;
    for(int i=30;~i;i--){
        int s=x>>i&1;
        if(son[p][!s]){//每次尽量走相反的节点
            res+=1<<i;
            p=son[p][!s];
        }
        else{
            p=son[p][s];
        }
    }
    return res;
}
int main(){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        insert(a[i]);
    }
    int ans=-1;
    for(int i=1;i<=n;i++){
        ans=max(ans,find(a[i]));
    }
    cout<<ans<<endl;
}

应用:AcWing 144.最长异或值路径

在这里插入图片描述
在这里插入图片描述

所谓的路径,可以想办法把它转化成两点或是两数之间的关系。那么中间的关联在于什么呢?当然是根节点。两点之间的异或路径长度,就是它们各自到根节点的异或路径长度的异或和(我这里是先简单地考虑到了两个节点在根节点两旁不同的子树上的情况,后面再想到路径重合的情况)。那么怎么证明这个算法的正确性呢?我们要发现异或运算的一些性质:
1.异或运算满足交换律与结合律
2.a xor a=0
3.a xor 0=a
这样就说明了两点间重合的路径的异或值为0,不会对结果造成影响。所以我们得出了一个算法:先用dfs求出所有节点到根节点的异或路径长,然后套用最大异或对模板求解即可。
代码如下:

#include<iostream>
using namespace std;
const int N=3e6+5;
typedef long long ll;
int head[N],to[2*N],ne[2*N],idx;
ll a[N],w[N];
void add(int x,int y,ll z){
    ne[++idx]=head[x];
    to[idx]=y;
    w[idx]=z;
    head[x]=idx;
}
void dfs(int u,int fa){
    for(int i=head[u];i;i=ne[i]){
        int j=to[i];
        if(j!=fa){
            a[j]=a[u]^w[i];
            dfs(j,u);
        }
    }
}
int son[N][2];
void insert(ll x){
    int p=0;
    for(int i=31;i>=0;i--){
        int u=x>>i&1;
        if(!son[p][u]) son[p][u]=++idx;
        p=son[p][u];
    }
}
ll find(ll x){
    int p=0;
    ll ans=0;
    for(int i=31;i>=0;i--){
        int u=x>>i&1;
        if(son[p][!u]){
            ans+=1<<i;
            p=son[p][!u];
        }
        else p=son[p][u];
    }
    return ans;
}
int main(){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        int x,y;
        ll z;
        cin>>x>>y>>z;
        add(x,y,z);
        add(y,x,z);
    }
    dfs(0,-1);
    idx=0;
    for(int i=1;i<n;i++){
        insert(a[i]);
    }
    ll ans=-1;
    for(int i=1;i<=n;i++){
        ll sb=find(a[i]);
        ans=max(ans,sb);
    }
    cout<<ans;
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值