1 条题解
-
0
自动搬运
来自洛谷,原作者为

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
- 上传者