死嗑 Trie 字典树

字典树模板

//前置条件
const int N=1e7+10;
int sz=1; //节点的编号


//建树
struct Trie {
	int ch[26]; //储存字母用 26,数字用 10,二进制用 2
	bool flag; //记录接收字符串是否到末尾
	//当只有一棵树时不用执行下面操作
	void clear(){memset(ch,0,sizeof(ch));flag=false;}
}tree[N];


//插入 
void insert(string s)
{
	int u=0,c;
	for(int i=0;i<s.length();i++)
	{
		c=s[i]-'a'; //以数字的方式储存,或者减去 '0'
		if(!tree[u].ch[c]) tree[u].ch[c]=sz++; //连续的顺序编号
		u=tree[u].ch[c]; //结点连接的子节点,用于储存单独的字符串
	}
	tree[u].flag=true; //接收字符串的末尾
}


//查询 
int find(string s)
{
	int u=0,c;
	for(int i=0;i<s.length();i++)
	{
		c=s[i]-'a';
		if(!tree[u].ch[c]) return 0; //没有接收到这个字符串
		u=tree[u].ch[c];
	}
	//若不是接收字符串的末尾
	if(tree[u].flag==false) return 0;
	/*
	通常这里会有其他的判断条件,根据题目来补充
	*/
}

懒得重新画图,干脆直接放草稿上来

在这里插入图片描述
解析
字典树也称为前缀树,常用功能有插入和查询
tree[i].ch[j]=k
i i i 代表结点编号(比如根节点是 0,结点 a 是 1,结点 c 是 5)
j j j 代表字母编号(比如 ‘a’ 对应 0,‘b’ 对应 1,‘c’ 对应 2)
k k k 代表节点编号(比如每个节点都有对应编号,按照 1234 的顺序)

于是他错误的点名开始了

在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;

const int N=1e7+10;
int sz=1;

struct Trie {
	int ch[26],cnt; //cnt 用于记录接收字符串出现的次数,初始为 0,重复出现则 cnt++
	bool flag;
}tree[N];

void insert(string s)
{
	int u=0,c;
	for(int i=0;i<s.length();i++)
	{
		c=s[i]-'a';
		if(!tree[u].ch[c]) tree[u].ch[c]=sz++;
		u=tree[u].ch[c];
	}
	tree[u].flag=true;
}

int find(string s) //第一次出现:1 多次出现:2 无此单词:3 
{
	int u=0,c;
	for(int i=0;i<s.length();i++)
	{
		c=s[i]-'a';
		if(!tree[u].ch[c]) return 3;
		u=tree[u].ch[c];
	}
	if(tree[u].flag==false) return 3;
	if(tree[u].cnt==0) {
		tree[u].cnt++;
		return 1;
	}
	return 2;
}

int main()
{
	int n;cin>>n;
	string str;
	while(n--)
	{
		cin>>str;
		insert(str);
	}
	cin>>n;
	while(n--)
	{
		cin>>str;
		int ans=find(str);
		if(ans==1) cout<<"OK"<<endl;
		else if(ans==2) cout<<"REPEAT"<<endl;
		else cout<<"WRONG"<<endl;
	}
	return 0;
}

思路
样例的草稿也就是上面的图

注意 find() 函数是在接收字符串的末尾,才判断是否重复出现过(cnt++)

Phone List

在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;

const int N=1e7+10;
string S[N];
int sz=1;

struct Trie {
	int ch[10],sum; //sum 用于记录结点连接的子节点的个数
	bool flag;
	void clear(){memset(ch,0,sizeof(ch));flag=false;sum=0;}
}tree[N];

void insert(string s)
{
	int u=0,c;
	for(int i=0;i<s.length();i++)
	{
		c=s[i]-'0'; //记得这里的字符是数字,因此减去 '0'
		if(!tree[u].ch[c]) tree[u].ch[c]=sz++;
		tree[u].sum++; //子节点的个数加 1 
		u=tree[u].ch[c];
	}
	tree[u].flag=true;
}

int find(string s)
{
	int u=0,c;
	for(int i=0;i<s.length();i++)
	{
		c=s[i]-'0';
		if(!tree[u].ch[c]) return 0;
		u=tree[u].ch[c];
	}
	if(tree[u].sum>0) return 1; //该结点有连接的节点,说明该字符串是连某个字符串的前缀
	return 0;
}

int main()
{
	int n;cin>>n;
	while(n--)
	{
		for(int i=0;i<1e5;i++) tree[i].clear(); //清空字典树  重新建树
		int k;cin>>k;
		string str;
		int ans=0;
		for(int i=0;i<k;i++)
		{
			cin>>S[i];
			insert(S[i]);
		}
		for(int i=0;i<k;i++)
		{
			ans=find(S[i]);
			if(ans==1) {
				cout<<"NO"<<endl;
				break;
			}
		}
		if(ans==0) cout<<"YES"<<endl;
	}
    return 0;
}

思路
小坑是有前缀则输出 NO,没有前缀则输出 YES

注意:tree[u].sum++u=tree[u].ch[c] 的前面,因为判断的是字符串的最后一位(可以画图理解)

The XOR Largest Pair

在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;

const int N=1e7+10;
int a[N],sz=1;

struct Trie {
	int ch[2];
}tree[N];

void insert(string s)
{
	int u=0,c;
	for(int i=0;i<s.length();i++)
	{
		c=s[i]-'0';
		if(!tree[u].ch[c]) tree[u].ch[c]=sz++;
		u=tree[u].ch[c];
	}
}

int find(string s)
{
	int u=0,c;
	int ans=0,num=pow(2,30);
	for(int i=0;i<s.length();i++)
	{
		c=(s[i]-'0')^1; //要找异或结果是 1 的数字,可以和 1 异或得到该数字
		if(tree[u].ch[c]) ans+=num; //加上对应的十进制位数字
		else c^=1; //否则没有该数字,则是只有对应的数字
		u=tree[u].ch[c];
		num/=2;
	}
	return ans;
}

string transfer(int num) //转为二进制字符
{
	string s="";
	while(num!=0) {
		s+=((num%2)+'0');
		num>>=1;
	}
	while(s.length()!=31) s+='0';
	reverse(s.begin(),s.end());
	return s;
}

int main()
{
	int n;cin>>n;
	for(int i=0;i<n;i++)
	{
		cin>>a[i];
		string str=transfer(a[i]);
		insert(str);
	}
	int mm=0;
	for(int i=0;i<n;i++)
	{
		string str=transfer(a[i]);
		mm=max(mm,find(str));
	}
	cout<<mm<<endl;
    return 0;
}

懒画,放稿

在这里插入图片描述
在这里插入图片描述

思路
首先骂一下本题的范围,N 的取值高达 1e7,我找了半天 bug 才找出来(恼)

本来应该 num = pow(2,31),但这样 num 越界(int 的范围是 231 - 1),所以 num = pow(2,30),那么 transfer() 函数就应该改为 cnt.length()!=31

[USACO08DEC]Secret Message G

在这里插入图片描述
在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;

const int N=1e7+10;
int sz=1;

struct Trie {
	int ch[2],sum;
	int flag; //注意这里是 int 类型! 
}trie[N];

void insert(string s)
{
	int u=0,c;
	for(int i=0;i<s.length();i++)
	{
		c=s[i]-'0';
		if(!trie[u].ch[c]) trie[u].ch[c]=sz++;
		u=trie[u].ch[c];
		trie[u].sum++; //加的是节点自身 
	}
	trie[u].flag++; //注意有重复信息 
}

int find(string s)
{
	int u=0,c,ans=0;
	for(int i=0;i<s.length();i++)
	{
		c=s[i]-'0';
		if(!trie[u].ch[c]) return ans;
		u=trie[u].ch[c];
		if(trie[u].flag>0) ans+=trie[u].flag;
	}
	ans-=trie[u].flag; //去重 
	ans+=trie[u].sum;
	return ans;
}

int main()
{
	int m,n;cin>>m>>n;
	char k;
	string str;
	int tmp,all;
	for(int i=0;i<m;i++)
	{
		str="";
		cin>>tmp;
		while(tmp--) {
			cin>>k;str+=k;	
		}
		insert(str);
	}
	for(int i=0;i<n;i++)
	{
		str="";
		cin>>tmp;
		while(tmp--) {
			cin>>k;str+=k;	
		}
		all=find(str);
		cout<<all<<endl;
	}
    return 0;
}

思路
题目有些难读懂,大体意思就是:给你 n 个信息和 m 个暗号,要你求出对于对于每条暗号,有多少信息和这条暗号有着相同的前缀(这个前缀就是暗号和信息长度较小的那个)

方法就是以 n 个字符串建字典树,用 m 个暗号匹配查询,当到达某个信息的末尾则 ans++(ans是相同前缀数),到达暗号的末尾则 ans+=sum(sum是含暗号前缀的剩下信息总数),注意要减去重复的信息数量

注意:tree[u].sum++u=tree[u].ch[c] 的后面,因为加的是字符串的字符自身(可以画图理解)

【重做】The XOR Largest Pair

在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;

const int N=1e7+10;	//比 31*1e5大就行了 
int sz=1;
int tmp[32];

struct Trie {
	int ch[2];
}tree[N];

void insert(int x)
{
	int u=0,c=0;
	memset(tmp,0,sizeof(tmp));
	while(x)
	{
		tmp[c++]=x&1;	//从最后一位开始记录 
		x>>=1; 
	}
	for(int j=31;j>=0;j--)	//最大有 32位,所以下标从 31开始 
	{
		if(!tree[u].ch[tmp[j]]) tree[u].ch[tmp[j]]=sz++;
		u=tree[u].ch[tmp[j]];
	}
}

long long find(int x)
{
	int u=0,c=0;
	memset(tmp,0,sizeof(tmp));
	while(x)
	{
		tmp[c++]=x&1;	//从最后一位开始记录 
		x>>=1; 
	}
	long long ans=0;
	for(int j=31;j>=0;j--)
	{
		if(!tree[u].ch[1-tmp[j]]) u=tree[u].ch[tmp[j]];
		else
		{
			u=tree[u].ch[1-tmp[j]];
			ans+=(1<<j);
		}
	}
	return ans;
}

int main()
{
	int n;cin>>n;
	int x;
	long long res=0;
	while(n--)
	{
		cin>>x;
		insert(x);
		res=max(res,find(x));
	}
	cout<<res<<endl;
    return 0;
}

思路
01 01 01字典树的模板,两个函数都是直接传入的参数是 i n t int int 而不是 s t r i n g string string,这样会快一点(也更方便一点),还有加了注释的地方需要理解一下,补充一点: 2 0 2^0 20 1 1 1 位、 2 1 2^1 21 2 2 2 . . . ... ... 2 31 2^{31} 231 32 32 32

Nikitosh 和异或(未)

在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;

const int N=12e6+10;	//Ai小于 2的 30次方,整体小于 30*4e5 
int tmp[32],sz=1;
int s[N];
long long l[N],r[N];

struct Trie {
	int ch[2];
	void clear(){memset(ch,0,sizeof(ch));}
}tree[N];

void insert(int x)
{
	int u=0,c=0;
	memset(tmp,0,sizeof(tmp));
	while(x)
	{
		tmp[c++]=x&1;
		x>>=1;
	}
	for(int j=31;j>=0;j--)
	{
		if(!tree[u].ch[tmp[j]]) tree[u].ch[tmp[j]]=sz++;
		u=tree[u].ch[tmp[j]];
	}
}

long long find(int x)
{
	int u=0;
	long long ans=0;
	// 因为 insert和 find的是同一个数字 因此不用清空 
	/*
	memset(tmp,0,sizeof(tmp));
	while(x)
	{
		tmp[c++]=x&1;
		x>>=1;
	}
	*/
	for(int j=31;j>=0;j--)
	{
		if(!tree[u].ch[1-tmp[j]]) u=tree[u].ch[tmp[j]];
		else
		{
			ans+=(1<<j);
			u=tree[u].ch[1-tmp[j]];
		}
	}
	return ans;
}

int main()
{
	int n;cin>>n;
	int x=0;
	long long res=0;
	for(int i=1;i<=n;i++) cin>>s[i];
	for(int i=1;i<=n;i++)
	{
		x^=s[i];
		insert(x);
		l[i]=max(l[i-1],find(x));
	}
	for(int i=0;i<N;i++) tree[i].clear();
	x=0;
	for(int i=n;i>=1;i--)
	{
		x^=s[i];
		insert(x);
		r[i]=max(r[i+1],find(x));
	}
	for(int i=1;i<n;i++)
	{
		res=max(res,l[i]+r[i+1]);
	}
	cout<<res<<endl;
	return 0;
}

过了样例,又满分AC的大佬代码

#include <cstdio>
#include <cstring>
#include <string>
using namespace std;

int pot, ispn;
char ch, sk[100];

typedef long long ll;
typedef unsigned long long ull;

#define il inline
#define ENDL putchar('\n')
#define PUT(ch) putchar(ch)
#define GET(ch) ch = getchar()
#define isd(ch) (ch >= 48 && ch <= 57)
#define mst(a, b) memset(a, b, sizeof(a))
#define rep(a, b, c) for (register int a = b; a <= c; ++ a)
#define drep(a, b, c) for (register int a = b; a >= c; -- a)

template <typename T>
il T Abs(T a) {
    return a < 0 ? -a : a;
}
template <typename T>
il T Min(T a, T b) {
    return a < b ? a : b;
}
template <typename T>
il T Max(T a, T b) {
    return a > b ? a : b;
}
template <typename T>
il void Swap(T &a, T &b) {
    a ^= b ^= a ^= b;
}
template <typename T>
il void Minn(T &a, T b) {
    a = a < b ? a : b;
}
template <typename T>
il void Maxx(T &a, T b) {
    a = a > b ? a : b;
}
template <typename T>
il void read(T &x) {
    x = 0;
    ispn = 0;
    GET(ch);

    while (!isd(ch)) {
        if (ch == '.')
            ispn = 1;

        GET(ch);
    }

    while (isd(ch))
        x = (x << 1) + (x << 3) + (ch ^ 48), GET(ch);

    if (ispn)
        x = -x;
}
template <typename T>
il void write(T x) {
    pot = 0;

    if (x < 0)
        x = -x, PUT('-');

    do {
        sk[++ pot] = x % 10 + 48;
    } while (x /= 10);

    while (pot)
        PUT(sk[pot --]);
}

const int N = 400010;

int n, a[N], c[N][2];
ll ans, lx[N], rx[N], sss[N];

int u, sum;
il void insert(int x) {
    drep(i, 30, 0) {
        u = ((x >> i) & 1);

        if (!c[i][u])
            c[i][u] = 1;
    }
}
il ll fnd(int x) {
    sum = 0;
    drep(i, 30, 0) {
        u = ((x >> i) & 1);

        if (c[i][u ^ 1])
            sum += (1 << i);
    }
    return sum;
}

int main() {
    read(n);
    insert(0);
    rep(i, 1, n) read(a[i]);
    rep(i, 1, n) sss[i] ^= a[i];
    rep(i, 1, n) {
        lx[i] = Max(lx[i - 1], fnd(sss[i]));
        insert(sss[i]);
    }
    mst(c, 0), mst(sss, 0);
    insert(0);
    drep(i, n, 1) sss[i] ^= a[i];
    drep(i, n, 1) {
        rx[i] = Max(rx[i + 1], fnd(sss[i]));
        insert(sss[i]);
    }
    rep(i, 1, n - 1) Maxx(ans, lx[i] + rx[i + 1]);
    write(ans);
    return 0;
}

思路
样例没过,但AC了,死活看不懂,看了大佬的代码,发现这就是 A C M e r ACMer ACMer 和 我这个渣渣的差距啊…
参考文章

[SCOI2016]背单词

在这里插入图片描述
在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N=5.1e5+10;		//N为字符长度总和 
ll ans;						//ans要用 long long
int sz=1,sum[N],last[N],cnt=1;
vector<int> g[N];

struct Trie {
	int ch[26];
	bool flag;
}tree[N];

void insert(string s)
{
	int u=0,c;
	for(int i=0;i<s.length();i++)
	{
		int c=s[i]-'a';
		if(!tree[u].ch[c]) tree[u].ch[c]=sz++;
		u=tree[u].ch[c];
	}
	tree[u].flag=true;
}

//虽然但是,这种递归方法太难理解了
void rebuild(int x)
{
	if(x&&tree[x].flag)
	{
		g[last[x]].push_back(x);	//g[x][]存重构树中点 x的儿子们
		last[x]=x;					//last[x]是原树里 x节点上方离它最近的红点(包括自己)
	}
	for(int i=0;i<26;i++)
	{
		if(tree[x].ch[i])
		{
			last[tree[x].ch[i]]=last[x];
			rebuild(tree[x].ch[i]);
		}
	}
}

bool cmp(const int &x,const int &y) {
	return sum[x]<sum[y];
}

//深搜重排 trie树 
void dfs(int x)
{
	sum[x]=1;						//sum[x]是以 x为根的子树的大小
	for(int i=0;i<g[x].size();i++)
	{
		dfs(g[x][i]);
		sum[x]+=sum[g[x][i]];
	}
	sort(g[x].begin(),g[x].end(),cmp);
}

//深搜遍历 trie树统计答案 
void finally(int x)
{
	int index=cnt++;
	for(int i=0;i<g[x].size();i++)
	{
		ans+=cnt-index;				//inedx是前缀位置,cnt是节点自己的位置
		finally(g[x][i]);
	}
}

int main()
{
	int n;cin>>n;
	string str;
	while(n--)
	{
		cin>>str;
		reverse(str.begin(),str.end());		//反转后 按照前缀处理即可
		insert(str);
	}
	rebuild(0);
	dfs(0);
	finally(0);
	cout<<ans<<endl;
	return 0;
}

思路:
思路来源
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这种题相比思路,我更多的学到的是代码的细节,每个函数所对应的作用在别的题目中都有可能会用到,因此多看看函数是如何实现的吧(不行就硬背 ),然后对于整体的思路我的理解如下(配合上面的草稿纸观看):
① 重建后的树就是上图的树,之所以 I I I A A A 的左边是因为子树 A A A 所含有的子节点是最多的
② 最终排序是按照层序遍历,即从左到右,将每个节点的值减去其结点的值(根结点值为 0 0 0),全部加起来即最终结果,经草稿纸验算,这样的结果是最小的,毕竟很容易想到小的数要排序在最上面

[HNOI2004]L语言

在这里插入图片描述
在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;

const int N=2e6+10;
int sz=1,f[N];
char s[N];
map<string,int> mmp;		//也能记录 char[] 

inline int read() {
  int x = 0, neg = 1; char op = getchar();
  while (!isdigit(op)) { if (op == '-') neg = -1; op = getchar(); }
  while (isdigit(op)) { x = 10 * x + op - '0'; op = getchar(); }
  return neg * x;
}

inline void print(int x) {
  if (x < 0) { putchar('-'); x = -x; }
  if (x >= 10) print(x / 10);
  putchar(x % 10 + '0');
}

struct Trie {
	int ch[26];
	bool flag;
}tree[N];

inline void insert()
{
	scanf("%s",s+1);
	int u=0,c;
	for(register int i=strlen(s+1);i>=1;i--)
	{
		c=s[i]-'a';
		if(!tree[u].ch[c]) tree[u].ch[c]=sz++;
		u=tree[u].ch[c];
	}
	tree[u].flag=true;
}

inline void find()
{
	scanf("%s",s+1);
	if(mmp[s+1])
	{
		print(mmp[s+1]);
		cout<<endl;
		return;
	}
	f[0]=true;//初始化 前 0个字符一定可以匹配
	for(register int i=1;i<=strlen(s+1);i++)
	{
		int u=0,c;
		f[i]=false; //先让 f[i]=false 省去了开始的 memset 
		for(register int j=i;j>=1;j--)
		{
			c=s[j]-'a';
			if(!tree[u].ch[c]) break;
			u=tree[u].ch[c];
			if(tree[u].flag==true)
			{
				f[i]|=f[j-1];	//若 f[j-1]是 1,f[i]就是 1,否则 f[i]是 0
				if(f[i]) break; //接的上就退出
			}
		}
	}
	for(register int i=strlen(s+1);i>=0;i--)
	{
		if(f[i])
		{
			mmp[s+1]=i;
			print(i);
			cout<<endl;
			return;
		}
	}
}

int main()
{
	int n,m;
	n=read();m=read();
	while(n--) insert();
	while(m--) find();
	return 0;
}

思路
一开始我以为很简单,我的思路就是前几个字符串构建树,后面几个字符串对树进行判断,每到一处能理解的地方就++,最后输出总和,结果我提交 W A WA WA 了几个点,后来我发现这样做错误的地方,就是存在重复的前缀(以下面的输入举例)

2 1
abc
abcd
abcd

本来结果应该是 4,但我原本的代码只会输出 3,因为当判断到 a b c abc abc 能理解就会++并退出,而这并不是最长前缀

改进方法
将原字符串倒着构建树,比如上面那个例子,将 c b a cba cba d c b a dcba dcba 构建树,判断 a b c d abcd abcd 时先遍历再倒着来,即先判断 a a a,再判断 b a ba ba,再判断 c b a cba cba,最后判断 d c b a dcba dcba,发现能理解,于是就输出 4
在这里插入图片描述
但是还有要注意的地方,比如草稿纸下面的例子 a b c d e f j abcdefj abcdefj,当判断 a b c d abcd abcd 能理解后,再判断 f j fj fj e f j efj efj 都能理解,那么该取哪个?我们只需要一开始对每个都设置 f [ i ] = f a l s e f[i]=false f[i]=false,当能理解时写下 f [ i ] ∣ = f [ j − 1 ] f[i]|=f[j-1] f[i]=f[j1] 即可,就是当前面的字符串( a b c d abcd abcd)也可以理解时,那么就可以将此处 f [ i ] f[i] f[i] 与前面的合并,如果前面的是 t r u e true true,那么此处也是 t r u e true true,否则是 f a l s e false false(合并不成功)【 ∣ | 是或运算符,两者至少有一个是 t r u e true true 则结果为 t r u e true true

再改进
其实上面的代码还是过不了洛谷的评测机,会 T L E TLE TLE 掉四个点(即使用了 m a p map map 和 快读快输),这时候我不得不改写字典树成普通的两个分开的数组(毕竟写了这么多题都很熟练了)如下:

struct Trie {
	int ch[26];
	bool flag;
}tree[N];

改成:

int tree[N][26];
bool flag[N];

最终代码

#include<bits/stdc++.h>
using namespace std;

const int N=2e6+10;
int sz=1,f[N];
char s[N];
map<string,int> mmp;

inline int read() {
  int x = 0, neg = 1; char op = getchar();
  while (!isdigit(op)) { if (op == '-') neg = -1; op = getchar(); }
  while (isdigit(op)) { x = 10 * x + op - '0'; op = getchar(); }
  return neg * x;
}

inline void print(int x) {
  if (x < 0) { putchar('-'); x = -x; }
  if (x >= 10) print(x / 10);
  putchar(x % 10 + '0');
}

int tree[N][26];
bool flag[N];

inline void insert()
{
	scanf("%s",s+1);
	int u=0,c;
	int len=strlen(s+1);
	for(register int i=len;i>=1;i--)
	{
		c=s[i]-'a';
		if(!tree[u][c]) tree[u][c]=sz++;
		u=tree[u][c];
	}
	flag[u]=true;
}

inline void find()
{
	scanf("%s",s+1);
	if(mmp[s+1])
	{
		print(mmp[s+1]);
		cout<<endl;
		return;
	}
	f[0]=true;
	int len=strlen(s+1);
	for(register int i=1;i<=len;i++)
	{
		int u=0,c;
		f[i]=false;
		for(register int j=i;j>=1;j--)
		{
			c=s[j]-'a';
			if(!tree[u][c]) break;
			u=tree[u][c];
			if(flag[u]==true)
			{
				f[i]|=f[j-1];
				if(f[i]) break; 
			}
		}
	}
	for(register int i=len;i>=0;i--)
	{
		if(f[i])
		{
			mmp[s+1]=i;
			print(i);
			cout<<endl;
			return;
		}
	}
}

int main()
{
	int n,m;
	n=read();m=read();
	while(n--) insert();
	while(m--) find();
	return 0;
}

最长异或路径(未)

在这里插入图片描述

#include<bits/stdc++.h>
#define IL inline
#define RI register int
#define maxn 100008
int trie[maxn*31][2],xo[maxn],ans,rt;
int val[maxn],n,head[maxn],tot;
struct code{int u,v,w;}edge[maxn<<1];
IL void read(int &x){
	int f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while(s<='9'&&s>='0'){x=x*10+s-'0';s=getchar();}
	x*=f;
}
IL void add(int x,int y,int z)
{
	edge[++tot].u=head[x];
	edge[tot].v=y;
	edge[tot].w=z;
	head[x]=tot;
	edge[++tot].u=head[y];
	edge[tot].v=x;
	edge[tot].w=z;
	head[y]=tot;
}
IL void build_trie(int x,int rt)
{
	for(RI i=1<<30;i;i>>=1)
	{
		bool c=x&i;
		if(!trie[rt][c])trie[rt][c]=++tot;
		rt=trie[rt][c];
	}
}
IL int query(int x,int rt)
{
	int ans=0;
	for(RI i=1<<30;i;i>>=1)
	{
		bool c=x&i;
		if(trie[rt][c^1])ans+=i,rt=trie[rt][c^1];
		else rt=trie[rt][c];
	}
	return ans;
}
IL void dfs(int u,int fa)
{
	for(RI i=head[u];i;i=edge[i].u)
	{
		if(edge[i].v!=fa)
		{
			xo[edge[i].v]=xo[u]^edge[i].w;
			dfs(edge[i].v,u);
		}
	}
}
int main()
{
	read(n);
	for(RI i=1,u,v,w;i<n;i++)read(u),read(v),read(w),add(u,v,w);
	dfs(1,0);
	for(RI i=1;i<=n;i++)build_trie(xo[i],rt);
	for(RI i=1;i<=n;i++)ans=std::max(ans,query(xo[i],rt));
	printf("%d",ans);
}

有用到 d f s dfs dfs 优化的 D i j k s t r a Dijkstra Dijkstra,我也忘得差不多了,等我复习一遍后再回来看

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值