[NOIP2017模拟]Xor

题目背景
OURCE:NOIP2015-SHY-7

题目描述
求一棵带边权的树的一条最大 Xor 路径的值。这里的“路径”不一定从根到叶子结点,中间一段路径只要满足条件也可以。

输入格式
第一行,一个整数 N ,表示一颗树有 N 个节点,接下来 N-1 行,每行三个整数 a,b,c 表示节点 a 和节点 b 之间有条权值为 c 的边。

输出格式
输出仅一行,即所求的最大值。

样例数据
输入
4
1 2 3
1 3 4
1 4 7
输出
7

备注
【数据范围】
对 40% 的输入数据 :数据退化为一条链;
另对 10% 的输入数据 :N≤1000;
对 100% 的输入数据 :1≤N≤100000, c≤ 231 -1。

分析:这种题都是套路(好像以前也做过就是忘了)。由异或的性质,任意两点的路径的异或和就是这两点到根节点的异或和相异或。所以只需求出每个节点到根节点的异或和,丢进一棵trie树,再一个一个在trie树中寻找有没有与它相反的二进制数(比如,现在你枚举到101,就应该在trie树中找到010来异或成111。当然,如果最长二进制数有10位,你应该找1111111010来异或成1111111111),与ans比较一下,取较大值,得出答案。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
#include<queue>
#include<set>
using namespace std;

int getint()
{
    int sum=0,f=1;
    char ch;
    for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
    if(ch=='-')
    {
        f=-1;
        ch=getchar();
    }
    for(;isdigit(ch);ch=getchar())
        sum=(sum<<3)+(sum<<1)+ch-48;
    return sum*f;
}

const int N=100010,L=35;
struct node
{
    int son[2];
}tree[N*L];
int n,len,ans,x,y,z;
int tot,first[N],nxt[N<<1],to[N<<1],w[N<<1];
int dep[N];

void addedge(int x,int y,int z)
{
    tot++;
    nxt[tot]=first[x];
    first[x]=tot;
    to[tot]=y;
    w[tot]=z;
    tot++;
    nxt[tot]=first[y];
    first[y]=tot;
    to[tot]=x;
    w[tot]=z;
}

void dfs(int u,int fa)
{
    for(int p=first[u];p;p=nxt[p])
    {
        int v=to[p];
        if(v!=fa)
        {
            dep[v]=dep[u]^w[p];
            dfs(v,u);
        }
    }
}

void buildtree(int val)
{
    int pos=0;
    for(int i=len-1;i>=0;--i)
    {
        int t=(val>>i)&1;
        if(!tree[pos].son[t])
            tree[pos].son[t]=++tot;
        pos=tree[pos].son[t];
    }
}

int find(int val)
{
    int pos=0;
    for(int i=len-1;i>=0;--i)
    {
        int t=(val>>i)&1;
        if(tree[pos].son[t^1])//如果能找到相反的二进制数
            pos=tree[pos].son[t^1],val=val|(1<<i);//或运算的意思是val那一位直接改成1,因为已经确定了能异或成1
        else
            pos=tree[pos].son[t],val=val^(t<<i);//不能找到就只好委曲求全走和自己相同的,那一位改成0
    }

    return val;
}

int main()
{
    freopen("xor.in","r",stdin);
    freopen("xor.out","w",stdout);

    n=getint();
    for(int i=1;i<n;++i)
    {
        x=getint(),y=getint(),z=getint();
        ans=max(ans,z);
        addedge(x,y,z);
    }

    dfs(1,0);//求根节点到每个节点的异或和

    while(ans)
        ans>>=1,len++;//计算最长的二进制数来确定trie树深度

    tot=0;
    for(int i=1;i<=n;++i)
        buildtree(dep[i]);//建trie树

    for(int i=1;i<=n;++i)//到trie树中查找
    {
        dep[i]=find(dep[i]);
        ans=max(ans,dep[i]);
    }

    cout<<ans<<'\n';
    return 0;
}

本题结。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值