1. 程式人生 > 實用技巧 >【演算法競賽進階指南】字典樹 The XOR Longest Path

【演算法競賽進階指南】字典樹 The XOR Longest Path

題目連結

題意

給出一棵有權樹,定義一個路徑的權值為這條路徑上所有邊權的異或和。
請求出最大的路徑權值。

思路

想著想著突然發現從任意一個節點 \(rt\) 開始dfs,對於每個節點 \(u_i\) 可以得到 rt---> \(u_i\) 這條路徑的權值\(val_i\)

這時我們任意選擇兩個節點 \(u,v\),可以發現 \(val_{u}\ xor\ val_{v}\) 就是 \(u\)\(v\) 的路徑權值。

這時題目就轉化成了,給出 N 個數字求兩個數字的最大異或和。

就跟【演算法競賽進階指南】字典樹 The XOR Largest Pair 一樣了。

程式碼

#include <algorithm>
#include <map>
#include <queue>
#include <stack>
#include <stdio.h>
#include <string.h>
#include <vector>
#define pb push_back
#define pii pair<int, int>
#define pll pair<int, int>
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int N = 2e5 + 10;
const int mod = 1e9 + 7;
using namespace std;

vector<pii> vec[N];
vector<ll> v;
void dfs(int u, int fa, ll now)
{
    if (u != fa)
        v.pb(now);
    for (int i = 0; i < vec[u].size(); i++) {
        pll tmp = vec[u][i];
        if (tmp.first == fa)
            continue;
        dfs(tmp.first, u, now ^ tmp.second);
    }
}
int s[N], trie[N * 10][2], tot = 1;
void insert()
{
    int p = 1;
    for (int i = 0; i < 32; i++) {
        int now = s[i];
        if (!trie[p][now]) {
            trie[p][now] = ++tot;
        }
        p = trie[p][now];
    }
}

ll solve()
{
    ll ans = 0, p = 1, cnt = 31;
    for (int i = 0; i < 32; i++, cnt--) {
        int now = 1 ^ s[i];
        if (trie[p][now]) {
            ans += (1LL << cnt);
        } else {
            now = s[i];
        }
        p = trie[p][now];
    }
    return ans;
}

int main()
{
    int n;
    scanf("%d", &n);
    for (int i = 1; i < n; i++) {
        int u, v, w;
        scanf("%d%d%d", &u, &v, &w);
        vec[u].pb({ v, w }), vec[v].pb({ u, w });
    }
    dfs(1, 1, 0);
    ll rel = 0;
    for (int i = 0; i < v.size(); i++) {
        for (int j = 0; j < 32; j++) {
            s[31 - j] = (v[i] & (1LL << j)) ? 1 : 0;
        }
        insert();
        rel = max(rel, solve());
    }
    printf("%lld\n", rel);
    return 0;
}