zoukankan      html  css  js  c++  java
  • 【trie树】【P4551】 最长异或路径

    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, '
    ', 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

    对于异或和一类的题目,考虑自身异或两遍对答案无贡献的情况。

    动态判断一个串是否存在可以使用踹树来维护。

  • 相关阅读:
    javascript 写一个随机范围整数的思路
    Promise中的next 另一个用法
    继上一篇随笔,优化3张以上图片轮播React组件
    低性能3张图片轮播React组件
    用函数式编程思维解析anagrams函数
    Python time time()方法
    torch.view().expand()
    pytorch中的top_K()函数
    设定学习率衰减
    两个集合求交集
  • 原文地址:https://www.cnblogs.com/yifusuyi/p/10269235.html
Copyright © 2011-2022 走看看