POJ 3764 The xor-longest Path ( 字典树求异或最值 && 异或自反性质 && 好题好思想)

题意 : 给出一颗无向边构成是树,每一条边都有一个边权,叫你选出一条路,使得此路所有的边的异或值最大。

分析 : 暴力是不可能暴力的,这辈子不可能暴力,那么来冷静分析一下如何去做。假设现在答案的异或值的最大的路的起点和终点分别为 a、b,这个异或值为了方便我用 ⊕(a, b) 表示,那么接下来有一个巧妙的转化 ⊕(a, b) == ⊕(root, a) ^ ⊕(root, b) ,这个转化在 LCA(a, b) == root 的时候明显成立,因为重复的部分会异或两次,相当于没有异或,而LCA(a, b) != root的时候,同样也可以使用上述转化来进行运算,因为这是一棵树,所以最后无论哪两个节点,都至少会有 root 是这两个节点的祖先,因此不必担心 a 和 b 在上述运算出来的结果会有 a 和 b 不联通的情况,那么这题就很好做了,只要从根节点开始DFS,每一次将新节点异或得到新的异或值并丢到字典树里面,这样所有的 ⊕(root, 所有点) 都会到字典树里面去,出来的结果也自然是其中的两个节点一个作为起点一个作为终点出来的路径异或值。表达可能不太好,请见谅,看看代码辅助理解吧……

#include<stdio.h>
#include<string.h>
#include<malloc.h>
#include<algorithm>
#define LL long long
using namespace std;
const int Bit = 30;
const int maxn = 100010;
int tot = 0;
int ans;
struct Edge{ int v, Next, weight; };
Edge e[maxn<<1];
int Head[maxn];
bool vis[maxn];

inline void Add_Edge(int v, int to, int weight)
{
    e[tot].v = to;
    e[tot].Next = Head[v];
    e[tot].weight = weight;
    Head[v] = tot++;
}

struct Trie
{
    Trie *ch[2];
    int v;
    inline void init(){
        for(int i=0; i<2; i++) this->ch[i] = NULL;
        this->v = -1;
    };
} Heap[maxn<<10];
int node_cnt = 0;
Trie *root;
Trie * New()
{
    Heap[node_cnt].init();
    return &Heap[node_cnt++];
}

inline void CreateTrie(int x)
{
    Trie * p = root, *tmp;
    for(int i=Bit; i>=0; i--){
        int idx = (x>>i)&1;
        if(p->ch[idx] == NULL){
            p->ch[idx] = New();
        }p = p->ch[idx];
    }
    p->v = x;
}

int Query(int x)
{
    Trie *p = root;
    for(int i=Bit; i>=0; i--){
        int idx = (x>>i)&1;
        if(p->ch[idx^1]){
            p = p->ch[idx^1];
        }else p = p->ch[idx];
    }
    return (p->v) ^ x;
}
void DFS(int u, int Val)
{
    CreateTrie(Val);///将路径的权值全丢到字典树里面去,代表⊕(root, u)
    vis[u] = true;
    for(int i=Head[u]; i!=-1; i=e[i].Next){
        int v = e[i].v;
        int w = e[i].weight;
        if(!vis[v]){
            ans = max(ans, Query(Val ^ w));///查询更新最值
            DFS(v, Val^w);
        }
    }
}

inline void INIT()
{
    node_cnt = 0;
    root = New();
    memset(Head, -1, sizeof(Head));
    memset(vis, false, sizeof(vis));
    ans = tot = 0;
}

int main(void)
{
    int n;
    while(~scanf("%d", &n)){
        INIT();
        for(int i=1; i<=n-1; i++){
            int u, v, w;
            scanf("%d %d %d" ,&u, &v, &w);
            u++, v++;
            Add_Edge(u, v, w);
            Add_Edge(v, u, w);
        }
        DFS(1, 0);
        printf("%d\n", ans);
    }
    return 0;
}

瞎 : 想法非常好的题目,利用异或性质转化思想需要学习

时间: 2024-10-10 08:54:32

POJ 3764 The xor-longest Path ( 字典树求异或最值 && 异或自反性质 && 好题好思想)的相关文章

POJ - Colored Sticks - 并查集+字典树

这道题主要还是要判断是不是欧拉图 说白了就是能不能这幅图能不能用一笔画下来,那么就可以知道了,如果是一个环状的,说明奇数度就不存在,否则就只能用两个奇数度(起点终点)//我的理解这是 只需要用字典树将单词变为对应的一个数字,然后并查集操作就可以,需要维护一个度变量 #include<stdio.h> #include<string.h> int du[500010],p[500010]; int tot=1; struct tree { tree *next[30]; int id

字典树求异或值

先上模板 1 typedef long long ll; 2 3 const int maxn=1e5+10; 4 5 struct t 6 { 7 ll tree[32*maxn][3];//字典树的树部分 8 ll val[32*maxn];//字典树的终止节点 9 ll cnt;//字典树的大小 10 void init() //初始化字典树 11 { 12 cl(tree,0); 13 cl(val,0); 14 cnt=1; 15 } 16 void add(ll a) 17 { 18

poj 3764 The xor-longest Path(字典树)

题目链接:poj 3764 The xor-longest Path 题目大意:给定一棵树,每条边上有一个权值,找出一条路径,使得路径上权值的亦或和最大. 解题思路:dfs一遍,预处理出每个节点到根节点路径的亦或和rec,那么任意路径均可以表示rec[a] ^ rec[b],所以问题 就转换成在一些数中选出两个数亦或和最大,那么就建立字典树查询即可. #include <cstdio> #include <cstring> #include <algorithm> us

POJ 3764 The xor-longest Path ( 字典树应用—— 求连续段相异或最大最小的线性算法)(好题)

题意:已知:给出n个结点的树,定义:两结点间的权值为两点之间所有边相异或的值.求:树中的某两点间的最大权值. 思路:先说简单一点的题:有道CowXor,是一串线性序列,求某连续段异或的最大值,这题的思路是先求前i项序列相异或的值Si,所以x到y的连续异或就是Sx^Sy ,因为a^b = (a ^ c) ^ (b ^ c). 这题同样是这个思路把线性拓展到树上,先求任何点到某一定点的连续异或值,比如选根结点0,所以这时候有两种情况,1.x,y的路径通过了根结点,显然正确.2.x,y的路径不通过根结

POJ 3764 The xor-longest Path 字典树求最大异或

题意,一颗树,每个边有个值,在树上找一条简单路径,使得这条路径上的边权异或值最大 把这题模型转换一下, 对于任意一条路径的异或,表示为f(u, v) 则f(u, v) = f(1, u) ^ f(1, v) 这是显然的 其中f(1, i)是可以再O(n)内处理出来 然后就是在一个数组内,找两个数异或值最大 然后就可以用字典树来搞 每个数变成01串,  然后插入字典树, 第30位在最前,然后29,依次到0位 就建立成了一个深度为31的字典树 对于一个询问,在字典树上找,就是尽量找跟其相反的路径.

poj3764 The XOR Longest Path【dfs】【Trie树】

The xor-longest Path Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 10038   Accepted: 2040 Description In an edge-weighted tree, the xor-length of a path p is defined as the xor sum of the weights of edges on p: ⊕ is the xor operator. W

【POJ 3764】 The xor-longest path

[题目链接] http://poj.org/problem?id=3764 [算法] 首先,我们用Si表示从节点i到根的路径边权异或和 那么,根据异或的性质,我们知道节点u和节点v路径上的边权异或和就是Sx xor Sy 问题就转化为了 : 在若干个数中,找到两个数异或的最大值,可以用Trie树加速,具体细节笔者不再赘述 [代码] #include <algorithm> #include <bitset> #include <cctype> #include <

Codeforces Round #482 (Div. 2)D. Kuro and GCD and XOR and SUM+字典树

题目链接:D. Kuro and GCD and XOR and SUM 题意:两种操作:第一种给数组添加一个数,第二种输入x,k,s,要求从数组中找到一个数v,要求k能整除gcd(k,v);并且v<=s-x,然后异或v与k的异或值最大. 题解:对与k大于1的情况我们暴力枚举过去,k为1的特殊处理建一颗字典树,如果可以的满足条件的话,每次取值时往相反方向取. 1 #include<bits/stdc++.h> 2 #include <iostream> 3 #include

HDU5715 XOR 游戏 二分+字典树+dp

当时Astar复赛的时候只做出1题,赛后补题(很长时间后才补,懒真是要命),发现这是第二简单的 分析: 这个题,可以每次二分区间的最小异或和 进行check的时候用dp进行判断,dp[i][j]代表前i个元素分成j个区间,j是最后一个区间的最后一个元素 如果dp[i][j]为真,表明每个区间长度大于L,异或和大于mid 否则为假 返回dp[n][m]就好 复杂度度 O(30^2*nm) 吐槽:和异或相关的题总是和字典树贪心有关,又是一道,铭记铭记 #include <stdio.h> #inc