1 条题解

  • 0
    @ 2025-8-24 22:01:15

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Terminus_Est
    expect the best and plan for the worst. | 退役了。

    搬运于2025-08-24 22:01:15,当前版本为作者最后更新于2019-03-08 19:25:09,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    蒟蒻第一次学习01trie,发篇题解加深一下印象。

    首先说一下题意。这题就是给你一棵树,求树上所有路径中异或和最大的。

    两个前置芝士:

    1、01trie

    把数拆成二进制的形式,然后每一位都只有两个字符:0或1,然后按照trie的方式存下来,以达到节省空间的效果。

    如下图:

    这样就不用一个数一个数组的浪费空间来存了QAQ

    2、异或

    把两个数写成二进制的形式,对照每一位,如果相同则这一位为0,否则为1。

    如下:

    3: 011

    4: 100

    3^4=111=7

    理解了我们就接下来进入正题。

    题解

    我们对于每一个数到根节点的异或和进行建01trie树。

    以样例为例子:

    然后有一个定论:一个数,如果它两次异或同一个数,那么它是不会有改变的。

    那么i~j的路径上的异或和,就可以表示成根到i的异或和异或上根到j的异或和。

    那思路就很明确了:对于每一位进行贪心,如果这一位有一个与它不同的,即异或 后是1,那我们就顺着这条路往下走;否则就顺着原路往下走。

    这样贪心为什么是对的呢?因为当前这一位的权值比后面所有位数加起来还要高。

    就比如有一个数它的二进制表示是10...0(n个0)

    那么它比01...1(n-1个1)还要大。

    所以最高位决定一切qwq~

    然后代码闪亮登场~

    #include<bits/stdc++.h>
    using namespace std;
    struct qwq{
        int v;
        int w;
        int nxt;
    }edge[2000001];
    int head[2000001];
    int cnt=-1;
    void add(int u,int v,int w){
        edge[++cnt].nxt=head[u];
        edge[cnt].v=v;
        edge[cnt].w=w;
        head[u]=cnt;
    }
    int sum[2000001];
    void dfs(int x,int fa){//预处理
        for(int i=head[x];~i;i=edge[i].nxt){
            int v=edge[i].v;
            int w=edge[i].w;
            if(v!=fa){
                sum[v]=sum[x]^w;
                dfs(v,x);
            }
        }
    }
    struct trie{
        int ch[2];
    }t[2000001];
    int tot;
    void build(int val,int x){
        for(int i=(1<<30);i;i>>=1){
            bool c=val&i;//取出二进制下这个数的当前位置
            if(!t[x].ch[c]){
                t[x].ch[c]=++tot;
            }
            x=t[x].ch[c];
        }
    }
    int query(int val,int x){
        int ans=0;
        for(int i=(1<<30);i;i>>=1){
            bool c=val&i;
            if(t[x].ch[!c]){//如果这一位可以进行异或就沿着这一条往下走
                ans+=i;
                x=t[x].ch[!c];
            }
            else x=t[x].ch[c];//否则就沿着另一条路往下走
        }
        return ans;
    }
    int main(){
        memset(head,-1,sizeof(head));
        int n;
        scanf("%d",&n);
        for(int i=1;i<n;++i){
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            add(u,v,w);
            add(v,u,w);
        }
        dfs(1,-1);//预处理出每一个节点到根的异或和
        for(int i=1;i<=n;++i)build(sum[i],0);//建立trie数
        int ans=0;
        for(int i=1;i<=n;++i){
            ans=max(ans,query(sum[i],0));//查询,取最大值
        }
        printf("%d\n",ans);
    } 
    

    复杂度O(n) 自带30的常数

    • 1

    信息

    ID
    3550
    时间
    1000ms
    内存
    512MiB
    难度
    4
    标签
    递交数
    0
    已通过
    0
    上传者