1. 程式人生 > >【trie樹】【P4551】 最長異或路徑

【trie樹】【P4551】 最長異或路徑

fin cas 最長 build ast spa 動態 疑惑 check

Description

給定 \(n\) 個點的帶邊權樹,求一條異或和最大的簡單路徑

Input

第一行是點數 \(n\)

下面 \(n - 1\) 行每行三個整數描述這棵樹

Output

輸出一個數代表答案

Hint

\(1~\leq~n~\leq~10^5~,~1~\leq~w~<~2^{31}\),其中 \(w\) 是最大邊權

Solution

考慮由於自身異或自身對答案無貢獻,對於兩個點 \(u,v\),他們簡單路徑上的異或和即為他們分別向根節點求異或和的兩個值的疑惑值。

然後考慮枚舉每個點,設它向根求異或和的值為 \(c\),尋找另一個能夠最大化異或值的點。顯然要從高到低考慮。高位不夠優秀的可以直接扔掉,我們考慮到底 \(i\)

位時,所有沒有被扔掉的點中,如果 \(c\) 的第 \(i\) 位為 \(1\),則為了讓答案更大,我們盡可能的選擇第 \(i\) 位為 \(0\) 的點,反過來同理。

於是問題變成了動態判斷是否存在符合要求的異或和。我們建立一棵 \(01\) trie來維護所有的異或和,然後在樹上反著走即可。

時空復雜度 \(O(n~\log w)\)。不過略微有點小卡空間

Code

#include <cstdio>
#include <algorithm>
#ifdef ONLINE_JUDGE
#define freopen(a, b, c)
#endif
#define ci const int
#define cl const long long

typedef long long int ll;

namespace IPT {
    const int L = 1000000;
    char buf[L], *front=buf, *end=buf;
    char GetChar() {
        if (front == end) {
            end = buf + fread(front = buf, 1, L, stdin);
            if (front == end) return -1;
        }
        return *(front++);
    }
}

template <typename T>
inline void qr(T &x) {
    char ch = IPT::GetChar(), lst = ' ';
    while ((ch > '9') || (ch < '0')) lst = ch, ch=IPT::GetChar();
    while ((ch >= '0') && (ch <= '9')) x = (x << 1) + (x << 3) + (ch ^ 48), ch = IPT::GetChar();
    if (lst == '-') x = -x;
}

template <typename T>
inline void ReadDb(T &x) {
    char ch = IPT::GetChar(), lst = ' ';
    while ((ch > '9') || (ch < '0')) lst = ch, ch = IPT::GetChar();
    while ((ch >= '0') && (ch <= '9')) x = x * 10 + (ch ^ 48), ch = IPT::GetChar();
    if (ch == '.') {
        ch = IPT::GetChar();
        double base = 1;
        while ((ch >= '0') && (ch <= '9')) x += (ch ^ 48) * ((base *= 0.1)), ch = IPT::GetChar();
    }
    if (lst == '-') x = -x;
}

namespace OPT {
    char buf[120];
}

template <typename T>
inline void qw(T x, const char aft, const bool pt) {
    if (x < 0) {x = -x, putchar('-');}
    int top=0;
    do {OPT::buf[++top] = char(x % 10 + '0');} while (x /= 10);
    while (top) putchar(OPT::buf[top--]);
    if (pt) putchar(aft);
}

const int maxn = 100010;
const int maxm = 200010;
const int maxt = 6200010;

struct Edge {
    Edge *nxt;
    int to, v;
};
Edge eg[maxm], *hd[maxn]; int ecnt;
inline void cont(ci from, ci to, ci v) {
    Edge &e = eg[++ecnt];
    e.to = to; e.nxt = hd[from]; e.v = v; hd[from] = &e;
}

struct Tree {
    Tree *son[2];
};
Tree qwq[maxt], *rot;
int top;

int n, ans;
int dist[maxn];

void reading();
void dfs(ci, ci);
void buildroot();
void build(Tree*, ci, ci);
int check(Tree*, ci, ci);

int main() {
    freopen("1.in", "r", stdin);
    qr(n);
    reading();
    dfs(1, 0);
    buildroot();
    for (int i = 1; i <= n; ++i) build(rot, dist[i], 31);
    for (int i = 1; i <= n; ++i) ans = std::max(ans, check(rot, dist[i], 31) ^ dist[i]);
    qw(ans, '\n', true);
    return 0;
}

void reading() {
    int a, b, c;
    for (int i = 1; i < n; ++i) {
        a = b = c = 0; qr(a); qr(b); qr(c);
        cont(a, b, c); cont(b, a, c);
    }
}

void dfs(ci u, ci fa) {
    for (Edge *e = hd[u]; e; e = e->nxt) {
        int &to = e->to;
        if (to == fa) continue;
        dist[to] = dist[u] ^ e->v; dfs(to, u);
    }
}

void buildroot() {
    rot = qwq; top = 1;
}

void build(Tree *u, ci v, ci cur) {
    if (cur < 0) return;
    int k = static_cast<bool>((1 << cur) & v);
    build(u->son[k] ? u->son[k] : u->son[k] = qwq + (top++), v, cur - 1);
}

int check(Tree *u, ci v, ci cur) {
    if (cur < 0) return 0;
    int k = (static_cast<bool>((1 << cur) & v)) ^ 1;
    return u->son[k] ? (check(u->son[k], v, cur - 1) | (k << cur)) : check(u->son[k ^ 1], v, cur - 1) | ((k ^ 1) << cur);
}

Summary

對於異或和一類的題目,考慮自身異或兩遍對答案無貢獻的情況。

動態判斷一個串是否存在可以使用踹樹來維護。

【trie樹】【P4551】 最長異或路徑