码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2018-06-28 22:59:51      阅读:317      评论:0      收藏:0      [点我收藏+]

标签:输出   ...   splay   mes   end   输入输出   insert   void   输入输出格式   

题目描述

给定一棵 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;
} 

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

标签:输出   ...   splay   mes   end   输入输出   insert   void   输入输出格式   

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

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!