有关trie树的一道作业
这是本蒟蒻的第一篇题解,有不足之处还请多多包涵(๑•̀ㅂ•́)و✧
先上题目
题目描述
国有 个主要城市,这 个主要城市通过 条双向道路联通,每条道路会有一个愉悦值 。 国除了有这 条道路外,在每两个城市之间,还会有一条双向传送道,每条传送道的愉悦值为 。
小 来到了 国,他决定对 国进行若干次游历,每次游历会从给定的起点走向给定的终点。小 除了可以走普通的道路之外,还可以走传送通道。可惜传送道的费用太高昂了,小 决定,每次游历最多只经过一次传送道。
随着时间的推移, 国的这 条道路也在不断地完善,因此有时候,所有道路的愉悦值会同时异或上一个值 。
小 定义,一次游历的愉悦值,为这次游历中小 经过的所有边的愉悦值的异或和。他希望你告诉他,每一次游历的愉悦值的最大值是多少?
你需要注意的是,一次游历的路线可以经过每个点或者每条边任意多次,但必须从起点走到终点,且最多经过一次传送道;起点和终点可能存在重合的情况;一次游历可以中途经过终点,但不结束这次游历。
输入格式
第一行两个整数 ,表示 国城市的个数和小 的游历次数。
接下来 行,每行三个整数 ,表示城市 与城市 之间有一条愉悦度为 的道路。
接下来的 行,每行先是一个整数 。如果 ,接下来两个正整数 ,表示小 将进行一次从 出发到 的游历;如果 ,接下来一个整数 ,表示所有道路的愉悦值异或上一个值 。
输出格式
对于每次询问,输出一行表示这次游历的最大的愉悦值。
样例输入
6 4
1 2 3
1 3 0
2 4 2
2 5 4
3 6 1
1 4 1
1 2 6
2 4
1 2 6
样例输出
7
6
7
数据范围
对于 % 的数据,满足 , , 。
题解
一看到这题疯狂的异或,我们就想到了肯定要用 树去做他(其实是因为上课刚讲的)。那么回归这题。先看题意,本质上讲,基本是肯定要走一次传送带的(不然他给你干嘛。。。),记起始端点 ,中间端点。那么我们实际上就是从到,然后从坐传送带到,在从到。同时,我们又知道的性质,同一个数两次等于没有这个数,所以到就可以转化为从到根节点再到。
所以题目本质上是要找四条从某个点到根的链,使得四条链的异或和最大,并且其中有两条链的端点是固定的。
既然是,那么肯定要用树。
考虑到时间复杂度的要求,又是从根节点到端点,那么我们就想到通过初始化的方式初始化出每一个节点到根节点的异或和,同时弄出深度(之后有用)。
为了时间复杂度,那么我们肯定不能求解过程中在树上找最大的
那么我们就想到先把所有有可能出现的值放进树里面,之后找的时候就可以实现了。也就是的遍历整棵树,把所有的可能的两条链值都放进去(同时这里可以开一个数组,已经放过的就不用再放了),那么查找的时候也只要将端点到根的值用来再树里查找就行了。
然而。。。这题就这么完了吗
注意到题目中还有一类操作,显然我们没有考率到所有路径异或上一个值的影响,但是我们不可能一异或之后就重新建树,那么我们想到刚才提过的的性质(只和次数的奇偶性有关),那么自然而然的就联想到和深度的关系。那么自然就是如果深度不同,到根的值就要再异或上一个值,反之则不用.
但是。。。这样就能了吗
这里我们又有了问题,在异或上一个值后,树本身已经改变了,而不是简简单单直接把到根节点的值异或上那个影响就可以的,那我们该怎么去区别这种影响呢
那么我们很自然的想到,只要把会受影响的值和不受影响的值分开来建树就好了。
那么我们在建树之前,先利用的性质,判断出那些值回收影响,并且用二维数组来记录
然后根据数组将这些值分别放进不同的树中
那么我们在查找时,只需要根据深度奇偶性的区别,放到对应的树分别查找就好了,一个用直接到根节点的值,另一个需要再异或一个值进去查找。
最后附上的代码
#include <iostream>
#include <stdio.h>
#include <cstring>
using namespace std;
const int Maxn=(5e5+5)*2;
const int Maxw=(1<<12)+5;
int vis[1<<24]={0},head[Maxn],nxt[Maxn],to[Maxn],val[Maxn],tot=0,depth[Maxn]={0},prexor[Maxn],key=0,n,m;
void dfs(int x,int father)
{
depth[x]=depth[father]+1;
for(int e=head[x];e;e=nxt[e])
{
int v=to[e];
if(v==father) continue;
prexor[v]=prexor[x]^val[e];
dfs(v,x);
}
}
void add(int x,int y,int w)
{
nxt[++tot]=head[x];head[x]=tot;to[tot]=y;val[tot]=w;
nxt[++tot]=head[y];head[y]=tot;to[tot]=x;val[tot]=w;
}
class Trie
{
public:
int trie[1<<24][2]={0};
int tot=0;
void insert(int val)
{
int x=0;
for(int i=(1<<12);i;i>>=1){
bool c=val&i;
if(!trie[x][c]){
trie[x][c]=++tot;
}
x=trie[x][c];
}
}
int query(int val,int x) {
int ans = 0;
for (int i = (1 << 12); i; i >>= 1) {
bool c = val & i;
if (trie[x][!c]) {
ans += i;
x = trie[x][!c];
} else x = trie[x][c];
}
return ans;
}
};
Trie t[2];
bool flag[2][5005], exist[2][5005];
void build()
{
for (int i = 1; i <= n; i++)
flag[depth[i] & 1][prexor[i]] = 1;
for (int i = 0; i < (1 << 12); i++)
for (int j = 0; j < (1 << 12); j++)
{
exist[0][i ^ j] |= ((flag[0][i] & flag[0][j]) | (flag[1][i] & flag[1][j]));
exist[1][i ^ j] |= ((flag[0][i] & flag[1][j]) | (flag[1][i] & flag[0][j]));
}
for (int i = 0; i < (1 << 12); i++)
{
if (exist[0][i])
t[0].insert(i);
if (exist[1][i])
t[1].insert(i);
}
}
int result(int u,int v)
{
int k = (depth[u] & 1) ^ (depth[v] & 1),p,q;
if(k==0) {
p = t[0].query(prexor[u] ^ prexor[v], 0);
q = t[1].query(prexor[u] ^ prexor[v] ^ key, 0);
}
else{
p = t[1].query(prexor[u] ^ prexor[v], 0);
q = t[0].query(prexor[u] ^ prexor[v] ^ key, 0);
}
return max(p,q);
}
int main() {
int x,y,w,opt,v;
cin>>n>>m;
for(int i=1;i<n;++i)
{
scanf("%d%d%d",&x,&y,&w);
add(x,y,w);
}
dfs(1,0);
build();
while(m-->0)
{
scanf("%d",&opt);
if(opt==1)
{
scanf("%d%d",&x,&y);
printf("%d\n",result(x,y));
}
else
{
scanf("%d",&v);
key=key^v;
}
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!