1. 程式人生 > 其它 >【Coel.學習筆記】【一個階段的結束】01-Trie樹(01字典樹)求異或路徑

【Coel.學習筆記】【一個階段的結束】01-Trie樹(01字典樹)求異或路徑

題前閒語

是的,變成閒語了(別問我為什麼要改)
今天考完了月考,雖然發揮得不是很好但終歸是結束了,休息一下~
剛好深進也到貨了,開始新一輪學習吧!

題目簡介

題目描述

給定一棵 \(n\) 個點的帶權樹,結點下標從 \(1\) 開始到 \(n\)。尋找樹中找兩個結點,求最長的異或路徑。
異或路徑指的是指兩個結點之間唯一路徑上的所有邊權的異或。

輸入輸出格式

輸入格式

第一行一個整數 \(n\),表示點數。
接下來 \(n-1\) 行,給出 \(u,v,w\) ,分別表示樹上的 \(u\) 點和 \(v\) 點有連邊,邊的權值是 \(w\)

輸出格式

一行,一個整數表示答案。

解題思路

考慮用鏈式前向星存圖,求出每個節點到根節點(自行定義根節點為\(1\)

)的異或路徑,那麼兩個節點的異或路徑就是它們與根節點路徑的異或值
先用\(dfs\)初始化每個節點的異或值:

struct edge {
    int next, to, w;
} edge[maxn];

int p[maxn], cnt;

void add_edge(int u, int v, int w) {
    edge[++cnt].next = p[u];
    edge[cnt].to = v;
    edge[cnt].w = w;
    p[u] = cnt;
}

void init(int x, int f) {//dfs
    for (int i = p[x]; i != 0; i = edge[i].next) {
        int v = edge[i].to, w = edge[i].w;
        if (v != f) {
            s[v] = s[x] ^ w;
            init(v, x);
        }
    }
}

然後暴力列舉。當然直接暴力列舉的話複雜度是\(O(n^2)\),沒法通過\(n=10^5\),所以需要用\(01-Trie\)優化。
(此處待補充)
程式碼如下:

#include <cctype>
#include <cstdio>
#include <iostream>
#include <vector>

namespace FastIO {
inline int read() {
    int x = 0, f = 1;
    char ch = getchar();
    while (!isdigit(ch)) {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (isdigit(ch)) {
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x * f;
}
inline void write(int x) {
    if (x < 0) {
        x = -x;
        putchar('-');
    }
    static int buf[35];
    int top = 0;
    do {
        buf[top++] = x % 10;
        x /= 10;
    } while (x);
    while (top)
        putchar(buf[--top] + '0');
    puts("");
}
}  // namespace FastIO

using namespace std;
using namespace FastIO;

const int maxn = 1e5 + 10;

struct edge {
    int next, to, w;
} edge[maxn];

int p[maxn];
int n, cnt, tot, ans;
int s[maxn], ch[maxn * 32][2];

void add_edge(int u, int v, int w) {
    edge[++cnt].next = p[u];
    edge[cnt].to = v;
    edge[cnt].w = w;
    p[u] = cnt;
}

void init(int x, int f) {
    for (int i = p[x]; i != 0; i = edge[i].next) {
        int v = edge[i].to, w = edge[i].w;
        if (v != f) {
            s[v] = s[x] ^ w;
            init(v, x);
        }
    }
}

void insert(int v) {
    int u = 0;
    for (int i = (1 << 30); i; i >>= 1) {
        bool c = v & i;
        if (!ch[u][c])
            ch[u][c] = ++tot;
        u = ch[u][c];
    }
}

int find(int v) {
    int ans = 0, u = 0;
    for (int i = (1 << 30); i; i >>= 1) {
        bool c = v & i;
        if (ch[u][!c]) {
            ans += i;
            u = ch[u][!c];
        } else
            u = ch[u][c];
    }
    return ans;
}

int main() {
    n = read();
    for (int i = 1; i <= n - 1; i++) {
        int u = read(), v = read(), w = read();
        add_edge(u, v, w);
        add_edge(v, u, w);
    }
    init(1, -1);
    for (int i = 1; i <= n; i++)
        insert(s[i]);
    for (int i = 1; i <= n; i++)
        ans = max(ans, find(s[i]));
    write(ans);
    return 0;
}