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

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

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

原文地址:https://www.cnblogs.com/yifusuyi/p/10269235.html

时间: 2024-11-08 22:06:48

【trie树】【P4551】 最长异或路径的相关文章

[luogu] P4551 最长异或路径(贪心)

P4551 最长异或路径 题目描述 给定一棵\(n\)个点的带权树,结点下标从\(1\)开始到\(N\).寻找树中找两个结点,求最长的异或路径. 异或路径指的是指两个结点之间唯一路径上的所有边权的异或. 输入输出格式 输入格式: 第一行一个整数\(N\),表示点数. 接下来 \(n-1\) 行,给出 \(u,v,w\) ,分别表示树上的 \(u\) 点和 \(v\) 点有连边,边的权值是 \(w\). 输出格式: 一行,一个整数表示答案. 输入输出样例 输入样例#1: 复制 4 1 2 3 2

P4551 最长异或路径 (01字典树,异或前缀和)

题目描述 给定一棵 nn 个点的带权树,结点下标从 11 开始到 NN .寻找树中找两个结点,求最长的异或路径. 异或路径指的是指两个结点之间唯一路径上的所有边权的异或. 输入输出格式 输入格式: 第一行一个整数 NN ,表示点数. 接下来 n-1n?1 行,给出 u,v,wu,v,w ,分别表示树上的 uu 点和 vv 点有连边,边的权值是 ww . 输出格式: 一行,一个整数表示答案. 输入输出样例 输入样例#1: 4 1 2 3 2 3 4 2 4 6 输出样例#1: 7 说明 最长异或序

洛谷 P4551 最长异或路径

题目描述 给定一棵 nn 个点的带权树,结点下标从 11 开始到 NN .寻找树中找两个结点,求最长的异或路径. 异或路径指的是指两个结点之间唯一路径上的所有节点权值的异或. 输入输出格式 输入格式: 第一行一个整数 NN ,表示点数. 接下来 n-1n?1 行,给出 u,v,wu,v,w ,分别表示树上的 uu 点和 vv 点有连边,边的权值是 ww . 输出格式: 一行,一个整数表示答案. 输入输出样例 输入样例#1: 4 1 2 3 2 3 4 2 4 6 输出样例#1: 7 说明 最长异

P4551 最长异或路径

题目描述 给定一棵 nnn 个点的带权树,结点下标从 111 开始到 NNN .寻找树中找两个结点,求最长的异或路径. 异或路径指的是指两个结点之间唯一路径上的所有边权的异或. 输入输出格式 输入格式: 第一行一个整数 NNN ,表示点数. 接下来 n−1n-1n−1 行,给出 u,v,wu,v,wu,v,w ,分别表示树上的 uuu 点和 vvv 点有连边,边的权值是 www . 输出格式: 一行,一个整数表示答案. 输入输出样例 输入样例#1: 4 1 2 3 2 3 4 2 4 6 输出样

B - The xor-longest Path P4551 最长异或路径

这道题其实可以用01Trie树来解决.平时我们所用的Trie树都是插入字符,而这里的Trie树只用0和1来表示,就成了一棵二叉树.最大的异或和实际上就是两个点到根节点异或和的异或和的最大值. 先dfs预处理出所有节点到根节点的异或和,在用这些异或和建一棵Trie树,最后在Trie树上贪心.对于一个数表示成二进制的每一位,我们尽量让它去异或与那一位上的值相反的值(0ˆ1,1ˆ0)枚举每一个数在Trie树上的前缀异或和,取个max即为最后的答案. 代码如下: #include<cstdio> #i

Luogu P4551 最长异或路径 01trie

做一个树上前缀异或和,然后把前缀和插到$01trie$里,然后再对每一个前缀异或和整个查一遍,在树上从高位向低位贪心,按位优先选择不同的,就能贪出最大的答案. #include<cstdio> #include<iostream> #include<algorithm> #include<cstring> #include<cmath> #include<cctype> #include<cstdlib> #include

LuoguP4551最长异或路径

LuoguP4551最长异或路径 题面 题目链接 题解 01 Trie 题目要求求树上的最长异或路径 很容易想到树上差分 处理每个点的根节点的异或和 讲异或和存进Trie树 按为贪心即可 代码如下: #include<bits/stdc++.h> using namespace std; const int MAXN = 100000 + 10; inline int read() { int f=1,x=0; char ch; do { ch=getchar(); if(ch=='-') f

最长异或路径(Trie,贪心)

传送门 题意:给定一棵n个点的带权树,结点下标从1开始到N.求树上最长的异或路径.异或路径指的是两个结点之间的路径上的所有边权的异或值的和. 分析:设dis[x]表示根节点到x的路径上所有边权的异或和,则有dis[x]=dis[father(x)]^w[x,father(x)],看到这个式子,有没有感到分外亲切?我们可以对树进行DFS,预处理出所有的dis[x]. 根据结论:树上x到y的路径上所有边权的异或和就等于dis[x]^dis[y].证明:若x和y分别在根节点的两棵子树上,则它们各自到根

基于 trie 树的最长匹配分词测试

测试一个基于trie树的分词,没有应用任何统计模型,分词的效率估计会很高但是分词准确率很差. reference http://www.hankcs.com/program/java/tire-tree-participle.html https://github.com/ansjsun/tree_split