1. 程式人生 > 實用技巧 >BZOJ-1954 Pku3764 The xor-longest Path(trie)

BZOJ-1954 Pku3764 The xor-longest Path(trie)

題目描述

  給定一棵 \(n(1\leq n\leq 10^5)\) 個節點的樹,樹上的每條邊都有一個權值。從樹中選擇兩個點 \(x\)\(y\),把從 \(x\)\(y\) 的路徑上的所有邊權 \(\text{xor}\)(異或)起來,求得到的最大結果。

分析

  設 \(D[x]\) 表示根節點到 \(x\) 的路徑上所有邊權的 \(\text{xor}\) 值,顯然有:

\[D[x]=D[\text{father}(x)]\text{ xor }w\big(x,\text{father(x)}\big) \]

  根據上式,我們可以對樹進行一次深度優先遍歷,求出所有的 \(D[x]\)

。不難發現,樹上 \(x\)\(y\) 的路徑上所有邊權的 \(\text{xor}\) 結果就等於 \(D[x]\text{ xor }D[y]\)。這是因為根據 \(\text{xor}\) 運算的性質($a\text{ xor }a=0 \(),”\)x$ 到根“ 和 ”\(y\) 到根“ 這兩條路徑重疊的部分恰好抵消掉。

  所以,問題就變成了從 \(D[1]\) ~ \(D[N]\)\(N\) 個數中選出兩個,\(\text{xor}\) 的結果最大,可以用 \(\text{trie}\) 樹來快速求解。

程式碼

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int trie[N*30][2],tot=1;
int D[N],head[N],num_edge;
struct Edge
{
    int to;
    int dis;
    int Next;
}edge[N<<1];
void add_edge(int from,int to,int dis)
{
    edge[++num_edge].to=to;
    edge[num_edge].dis=dis;
    edge[num_edge].Next=head[from];
    head[from]=num_edge;
}
void dfs(int x,int fa,int sum)
{
    D[x]=sum;
    for(int i=head[x];i;i=edge[i].Next)
    {
        int y=edge[i].to,z=edge[i].dis;
        if(y!=fa)
            dfs(y,x,sum^z);
    }
}
void insert(int val)
{
    int p=1;
    for(int i=30;i>=0;i--)
    {
        int num=(val>>i)&1;
        if(trie[p][num]==0)
            trie[p][num]=++tot;
        p=trie[p][num];
    }
}
int search(int val)
{
    int p=1;
    long long ans=0;
    for(int i=30;i;i--)
    {
        int num=(val>>i)&1;
        if(trie[p][!num])
        {
            ans=ans+(1<<i);
            p=trie[p][!num];
        }
        else
            p=trie[p][num];
    }
    return ans;
}
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n-1;i++)
    {
        int u,v,w;
        scanf("%d %d %d",&u,&v,&w);
        add_edge(u,v,w);
        add_edge(v,u,w);
    }
    dfs(1,0,0);
    for(int i=1;i<=n;i++)
        insert(D[i]);
    int ans=0;
    for(int i=1;i<=n;i++)
        ans=max(ans,search(D[i]));
    cout<<ans<<endl;
    return 0;
}