如何求出树上的某两个点的最大路径总异或和?

对于树上的两点之间的简单路径,求经过边权值的最大异或和
首先考虑一个点到根节点所经过路径的异或和,可以深搜求出
考虑两点简单路径的异或和,其值一定是两个点分别到根节点的异或和的异或,那么此时我们已知一个点,如果求出另一个点使得两点之间的异或和最大呢?
考虑字典树存取,深搜完之后,将每个点到根节点的异或和都存到字典树中,对于一个点,我们可以直接在字典树上进行查找,按位进行判断,如果存在以x为根节点的儿子,那么就向下一层,直到遍历到叶节点为止,这样一定是最大的异或和,因为这样可以保证从高位向低位尽可能的都异或
例题:例题
对于询问一,我们直接用一个变量记录一下即可
对于询问二,首先思考,如果我们找的另一个点与这个点的经过的边数是偶数,那么很明显的之前的询问一均无效,因为异或和为0,对于偶数,那么就要异或上它,求一下两者最大值
我们在深搜求距离根节点的异或和过程中,还要记录一个深度,也就是深度为奇还是偶,只有这样,我们才能确定两个点的距离的奇偶性,此外,在询问前还要把本节点删掉,防止自己向自己连边

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e7+10,mod=1e9+7;
int trie[N][2],cnt[N][2];
int tot=0;
int newNode(){
    int x=++tot;
    trie[x][0]=trie[x][1]=0;
    cnt[x][0]=cnt[x][1]=0;
    return x;
}
void add(int x,int d,int t=1){
    int p=1;
    cnt[p][d]+=t;
    for(int i=30;i>=1;i--){
        int u=(x>>(i-1)&1);
        if(!trie[p][u]){
            trie[p][u]=newNode();
        }
        p=trie[p][u];
        cnt[p][d]+=t;
    }
}
int query(int x,int d){
    int p=1;
    if(!cnt[p][d]){
        return 0;
    }
    int ans=0;
    for(int i=30;i>=1;i--){
        int u=(x>>(i-1)&1);
        if(cnt[trie[p][u^1]][d]){
            ans|=(1<<(i-1));
            p=trie[p][u^1];
        }
        else p=trie[p][u];
    }
    return ans;
}
void solve(){
    int n,m; cin>>n>>m;
    vector<pair<int,int>>g[n+1];
    for(int i=1;i<n;i++){
        int a,b,c; cin>>a>>b>>c;
        g[a].push_back({b,c});
        g[b].push_back({a,c});
    }
    vector<int>a(n+1),d(n+1);
    function<void(int,int)>dfs;
    dfs=[&](int u,int fa)->void{
        for(auto now:g[u]){
            int x=now.first,w=now.second;
            if(x==fa) continue;
            d[x]=d[u]^1;
            a[x]=a[u]^w;
            dfs(x,u);
        }
    };
    dfs(1,0);
    tot=0;
    newNode();
    for(int i=1;i<=n;i++){
        add(a[i],d[i]);
    }
    int w=0;
    while(m--){
        char op; cin>>op;
        if(op=='^'){
            int y; cin>>y;
            w^=y;
        }
        else{
            int u,x; cin>>u>>x;
            add(a[u],d[u],-1);
            cout<<max(query(a[u]^x,d[u]),query(a[u]^x^w,d[u]^1))<<" ";
            add(a[u],d[u]);
        }
    }
    cout<<'\n';
}
signed main(){
    std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);int t;cin>>t;while(t--)solve();
}

补充, 对于该题还有一个对应的模板题: 最长异或路径

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6 + 10, mod = 1e9 + 7;
int tot;
int trie[N][2], cnt[N];
vector<pair<int, int>> g[N];
int newNode(){
    int x = ++ tot;
    return x;
}
void add(int x, int t = 1){
    int p = 1;
    cnt[p] += t;
    for(int i = 32; i >= 1; i--){
        int u = (x >> (i - 1) & 1);
        if(!trie[p][u]){
            trie[p][u] = newNode();
        }
        p = trie[p][u];
        cnt[p] += t;
    }
}
int query(int x){
    int p = 1;
    if(!cnt[p]) return 0;
    int res = 0;
    for(int i = 32; i >= 1; i--){
        int u = (x >> (i - 1) & 1);
        if(cnt[trie[p][u ^ 1]]){
            res |= (1 << (i - 1));
            p = trie[p][u ^ 1];
        }
        else p = trie[p][u];
    }
    return res;
}
signed main()
{
    std::ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int n; cin >> n;
    for(int i = 1; i <= n - 1; i++){
        int u, v, w; cin >> u >> v >> w;
        g[u].push_back({v, w});
        g[v].push_back({u, w});
    }    
    vector<int> d(n + 1);
    function<void(int, int)> dfs;
    dfs = [&](int u, int fa) -> void{
        for(auto now : g[u]){
            int x = now.first;
            if(x == fa) continue;
            d[x] = d[u] ^ now.second;
            dfs(x, u);
        }
    };
    dfs(1, 0);
    newNode(); // 做根节点
    for(int i = 1; i <= n; i++) add(d[i]);
    int res = 0;
    for(int i = 1; i <= n; i++){
        res = max(res, query(d[i]));
    }
    cout << res;
    return 0;
}
posted @ 2024-06-07 17:31  o-Sakurajimamai-o  阅读(30)  评论(0编辑  收藏  举报
-- --