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

说明

最长异或序列是1-2-3,答案是 7 (=3 ⊕ 4)

数据范围

1≤n≤100000; 0<u,v≤n; 0≤w<2^31

Solution

这道题,套路题啊...
首先要考虑到两个思路:

  • 异或的基本性质 :
  • 01字典树

1.异或的基本性质:

\[{(x+y)}\bigoplus{y}=x\]

2.带修改的01字典树

考虑枚举每一个点对,但是数据范围太大。
此时我们可以使用01字典树,步骤如下:

  • 先将边权转化为点权,预先处理出从根节点到每一个点的异或前缀和。
  • 然后将所有点的异或前缀和插入字典树中。
  • 再进行查询,注意查询时要将查询的点暂时删除。
  • 查询最大值即为答案。

    代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=200008;
int ch[32*maxn][2];
ll val[32*maxn],c[maxn];
int num[32*maxn];
int sz,n;
ll b[maxn];

void insert(ll a)
{
    int u=0;
    for(int i=32;i>=0;i--)
    {
        int c=((a>>i)&1);
        if(!ch[u][c])
        {
            memset(ch[sz],0,sizeof(ch[sz]));
            val[sz]=0;
            num[sz]=0;
            ch[u][c]=sz++;
        }
        u=ch[u][c];
        num[u]++;
    }
    val[u]=a;
}

void update(ll a,int d)
{
    int u=0;
    for(int i=32;i>=0;i--)
    {
        int c=((a>>i)&1);
        u=ch[u][c];
        num[u]+=d;
    }
}

ll query(ll x)
{
    int u=0;
    for(int i=32;i>=0;i--)
    {
        int c=((x>>i)&1);
        if(ch[u][c^1]&&num[ch[u][c^1]])
        u=ch[u][c^1];
        else u=ch[u][c];
    }
    return x^val[u];
}

struct sj{
    int to;
    int next;
    int w;
}a[maxn];
int size,head[maxn];

void add(int x,int y,int z)
{
    a[++size].to=y;
    a[size].w=z;
    a[size].next=head[x];
    head[x]=size;
}

int v[maxn],now,ans=-1;
void dfs(int x)
{
    v[x]=1;
    c[x]=now;
    for(int i=head[x];i;i=a[i].next)
    {
        int tt=a[i].to;
        if(!v[tt])
        {
            now^=a[i].w;
            dfs(tt);
            now^=a[i].w;
        }
    }
}

int main()
{
    cin>>n;
    for(int i=1;i<n;i++)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z); add(y,x,z);
    }
    dfs(1);
    for(int i=1;i<=n;i++)
    insert(c[i]);
    for(int i=1;i<=n;i++)
    {
        update(c[i],-1);
        int kk=query(c[i]);
        ans=max(ans,kk);
        update(c[i],1);
    }
    cout<<ans<<endl;
} 

原文地址:https://www.cnblogs.com/Kv-Stalin/p/9240958.html

时间: 2024-10-19 00:08:47

P4551 最长异或路径 (01字典树,异或前缀和)的相关文章

异或最大值(01字典树)

/** 异或最大值(01字典树) 题意:求n个非负数中任意2个的异或值的最大值.n数量级为10^5 分析:直接暴力肯定超时了.一个非负整数可以看成1个32位的01字符串,n个数可以看成n个字符串,因此可以建立字典树, 建好树后,对于任意非负整数x,可以沿着树根往下贪心找到y,使得x异或y最大,复杂度为树的深度. */ #include <stdio.h> #include <string.h> #include <algorithm> #include <iost

[01字典树]求序列完美度(求区间最大异或值)

https://nanti.jisuanke.com/t/15531 解题关键:01字典树模板,用字典树保存每个数的二进制表示,从而动态维护区间上的最大异或值,注意添加和删除都可以用于一个change函数表示. 复杂度:$O(n\log n + {n^2}\log n)$ 1 #include<bits/stdc++.h> 2 #define maxn 1005 3 #define inf 0x3f3f3f3f 4 using namespace std; 5 typedef long lon

Chip Factory---hdu5536(异或值最大,01字典树)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5536 题意:有一个数组a[], 包含n个数,从n个数中找到三个数使得 (a[i]+a[j])⊕a[k]最大,i,j,k不同; 求异或的结果最大所以我们可以用01字典树,先把所有的数加入字典树中,从n个数中选出两个数a[i]和a[j], 先把他们从字典树中删除,然后找到与a[i]+a[j]异或最大的数,和结果取最大值即可: 最后不要忘记再把a[i]和a[j]添加到字典树中即可: #include<st

利用01字典树查询最大异或值

01字典树的是只含有0和1两种字符的字典树,在使用它的时候,把若干数字转成二进制后插入其中 在查询树中的哪个数字和给定数字有最大异或值的时候,从根开始贪心查询就ok了 HDU4825是一道裸题:给出n个数和m次询问,每次询问给出一个数x,问在n个数中哪个数与x异或值最大 1 #include<cstdio> 2 #include<cstring> 3 const int maxn=1000005; 4 int n,m,rt; 5 int a[maxn],v[3500005],s[3

HDU6191(01字典树启发式合并)

Query on A Tree Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 132768/132768 K (Java/Others)Total Submission(s): 801    Accepted Submission(s): 302 Problem Description Monkey A lives on a tree, he always plays on this tree. One day, monkey

2014百度之星资格赛—— Xor Sum(01字典树)

Xor Sum Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 132768/132768 K (Java/Others) Total Submission(s): 0    Accepted Submission(s): 0 Problem Description Zeus 和 Prometheus 做了一个游戏,Prometheus 给 Zeus 一个集合,集合中包含了N个正整数,随后 Prometheus 将向 Zeus 发起

HDU 4825 Xor Sum 【01字典树】

题面: Problem Description Zeus 和 Prometheus 做了一个游戏,Prometheus 给 Zeus 一个集合,集合中包含了N个正整数,随后 Prometheus 将向 Zeus 发起M次询问,每次询问中包含一个正整数 S ,之后 Zeus 需要在集合当中找出一个正整数 K ,使得 K 与 S 的异或结果最大.Prometheus 为了让 Zeus 看到人类的伟大,随即同意 Zeus 可以向人类求助.你能证明人类的智慧么? Input 输入包含若干组测试数据,每组

HDU4825/5536 [01 字典树/简单字典树更新]

Xor Sum Time Limit: 1000 MS Zeus 和 Prometheus 做了一个游戏,Prometheus 给 Zeus 一个集合,集合中包含了N个正整数,随后 Prometheus 将向 Zeus 发起M次询问,每次询问中包含一个正整数 S ,之后 Zeus 需要在集合当中找出一个正整数 K ,使得 K 与 S 的异或结果最大.Prometheus 为了让 Zeus 看到人类的伟大,随即同意 Zeus 可以向人类求助.你能证明人类的智慧么? Input 输入包含若干组测试数

01字典树及其模板

模板 : const int Bit = 32; struct Trie { Trie *ch[2]; LL v; inline void init(){ for(int i=0; i<2; i++) this->ch[i] = NULL; this->v = -1; } }; Trie *root;///记得在 main 函数的时候分配空间给 root 并初始化 inline void CreateTrie(LL x) { Trie * p = root, *tmp; for(int