洛谷P4551 最长异或路径 trie
题目描述
给定一棵\(n\)个点的带权树,结点下标从\(1\)开始到\(N\)。寻找树中找两个结点,求最长的异或路径。
异或路径指的是指两个结点之间唯一路径上的所有边权的异或。
输入格式
第一行一个整数\(N\),表示点数。
接下来 \(n−1\) 行,给出 \(u\),\(v\),\(w\) ,分别表示树上的 \(u\) 点和 \(v\) 点有连边,边的权值是 \(w\)。
输出格式
一行,一个整数表示答案。
输入输出样例
输入 #1
4
1 2 3
2 3 4
2 4 6
输出 #1复制
7
说明/提示
最长异或序列是1-2-3,答案是 7 (=3 ⊕ 4)
数据范围
\(1\le n \le 100000;0 < u,v \le n;0 \le w < 2^{31}\)
分析
先利用xor的性质:\(x \oplus y \oplus y=x\),提示我们只需要一遍电风扇dfs计算所有点到根节点(随便用1来当根节点)的\(xor\)路径即可。那么现在就可以\(O(n^2)\)做这道题了。
当然\(O(n^2)\)过\(10^5\)是不可能的(可以一试嘿
考虑用\(trie\)进行优化。
设\(f_i\)为\(i\)号节点到根节点的\(xor\)路径,对于每一个\(f_i\)直接插入\(0/1trie\),查询时从高位到低位贪心:
- 如果当前节点存在一个与要查询的\(f_i\)的这一位相反的儿子,那么就走向这一个节点,\(xor\)值的该位赋为一。
- 反之走向另一个节点,因为这一层走的节点与\(f_i\)的这一位相同,相同的值\(xor=0\)。
那么只要从每一个点进行一遍查询,即可查询到从这个点出发能够得到的最大\(xor\)路径。
取最大值即可。
Code
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<queue>
#include<vector>
#define IL inline
#define re register
#define LL long long
#define ULL unsigned long long
#define re register
#define debug printf("Now is %d\n",__LINE__);
using namespace std;
template<class T>inline void read(T&x)
{
char ch=getchar();
while(!isdigit(ch))ch=getchar();
x=ch-'0';ch=getchar();
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
}
inline int read()
{
int x=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
x=ch-'0';ch=getchar();
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return x;
}
int G[55];
template<class T>inline void write(T x)
{
int g=0;
if(x<0) x=-x,putchar('-');
do{G[++g]=x%10;x/=10;}while(x);
for(re int i=g;i>=1;--i)putchar('0'+G[i]);putchar('\n');
}
int n;
int head[200010],ver[200010],nxt[200010],w[200010];
int cnt;
void insert(int a,int b,int c)
{
nxt[++cnt]=head[a];
head[a]=cnt;
ver[cnt]=b;
w[cnt]=c;
nxt[++cnt]=head[b];
head[b]=cnt;
ver[cnt]=a;
w[cnt]=c;
}
//dfs
int f[100010];
void dfs(int x,int now,int fa)
{
f[x]=now;
for(int i=head[x];i;i=nxt[i])
{
if(ver[i]==fa) continue;
dfs(ver[i],now^w[i],x);
}
}
//trie
int trie[5000010][2];
int trie_cnt;
void insert(int x)
{
int now=0;
for(int i=31;i>=0;i--)
{
if(!trie[now][(x>>i)&1]) trie[now][(x>>i)&1]=++trie_cnt;
now=trie[now][(x>>i)&1];
}
}
int ask(int x)
{
int now=0,res=0;
for(int i=31;i>=0;i--)
{
bool y=(x>>i)&1;
if(trie[now][!y]) now=trie[now][!y],res+=1<<i;
else now=trie[now][y];
}
return res;
}
int main()
{
n=read();
for(int i=1,a,b,c;i<n;i++)
{
a=read();
b=read();
c=read();
insert(a,b,c);
}
dfs(1,0,0);
for(int i=1;i<=n;i++) insert(f[i]);
int ans=0;
for(int i=1;i<=n;i++) ans=max(ans,ask(f[i]));
cout<<ans;
return 0;
}
小结
过了样例,但是第一遍写的时候忘记写\(ask\)中的那一个else了!导致调了半天都不知道哪里错了,叫Tringmo过来一看他就发现了。
/kk

浙公网安备 33010602011771号