最长异或值路径

最长异或值路径

给出一棵有n个节点的树,设\(w[i][j]\)为i,j之间的边权,定义一条路径的距离,为这条路径上所有边权的异或和,请你选出任意两个点,最小化这两点之间的路径距离,并求出这个最大值,\(1≤n≤100000\)

树的问题,无根变有根,更加好研究,通常维护出每个节点到根节点的信息,于是设\(s[i]\)表示点i到根节点的路径的权值的异或和,这个一遍dfs就可以维护出来,我不再赘诉了。

此时发现对于任意两个点i,j之间的距离就变成\(w[i]\wedge w[j]\),原因在于相同的数异或相同的数结果为0,而\(w[i]\wedge w[j]\),恰好因为这个性质,除去了i,j最近公共祖先到根节点的距离,剩下的距离自然都是i,j的距离了。

于是现在问题变成从集合\(\{w[i]\}\)选出两个数,最大化它们的异或和,显然我们只要维护一棵trie树,将这些数当做01串从高位到低位插入trie树,然后对于每个数从trie树从上往下尽量选择让该数的这一位不为0的数字即可。

参考代码:

#include <iostream>
#include <cstdio>
#define il inline
#define ri register
#define Size 100050
using namespace std;
struct point{
    point *next;int to,w;
}*pt,*head[Size];
bool is[Size];
int dis[Size],trie[Size*31][2],tot;
void dfs(int);
il int ask(int);
il void read(int&),
    link(int,int,int),
    insert(int);
template<class free>
il free Max(free,free);
int main(){
    int n,ans(0);read(n);
    for(int i(1),u,v,w;i<n;++i)
        read(u),read(v),read(w),
            link(u,v,w),link(v,u,w);dfs(0);
    for(int i(1);i<=n;++i)
        ans=Max(ans,ask(dis[i]));
    printf("%d",ans);
    return 0;
}
template<class free>
il free Max(free a,free b){
    return a>b?a:b;
}
il int ask(int x){
    int ans(0),p(0);
    for(int i(30);i>=0;--i)
        if(trie[p][x>>i&1^1])
            p=trie[p][x>>i&1^1],
                ans|=1<<i;
        else p=trie[p][x>>i&1];
    return ans;
}
il void insert(int x){int p(0);
    for(int i(30);i>=0;--i){
        if(!trie[p][x>>i&1])
            trie[p][x>>i&1]=++tot;
        p=trie[p][x>>i&1];
    }
}
void dfs(int x){is[x]|=true,insert(dis[x]);
    for(point *i(head[x]);i!=NULL;i=i->next){
        if(is[i->to])continue;
        dis[i->to]=dis[x]^i->w,dfs(i->to);
    }
}
il void link(int u,int v,int w){
    pt=new point{head[u],v,w},head[u]=pt;
}
il void read(int &x){
    x^=x;ri char c;while(c=getchar(),c<'0'||c>'9');
    while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
}

猜你喜欢

转载自www.cnblogs.com/a1b3c7d9/p/11248071.html