洛谷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

posted @ 2020-11-01 20:31  Vanilla_chan  阅读(175)  评论(0)    收藏  举报