题意
给定一颗 \(n\) 个点的树,每个点有一个点权。
先给出 \(m\) 组询问,每组询问给出一个三元组: \((x,y,z)\),表示:
询问在顶点 \(x\) 到 \(y\) 的路径上(包括 \(x,y\) )的所有点权中与 \(z\) 异或产生的最大值。
题解
看到“异或产生的最大值”,应该敏锐地想到 Trie
。
但是一般的 01Trie 只能处理数集已经确定的情况下,然而此题询问的范围是不确定的。
因此需要 可持久化 Tire
来解决这个问题。
定义: \(S_x\) 表示从根节点到 \(x\) 顶点的路径(包括两端)上的 前缀 Trie 树
。即这个 Trie 树 中保存了这条路径上所有节点的权值信息。
那么这么得到询问所给路径的权值信息的 Trie 树呢?
我们有这样一个公式:
\[S_{\text{所求}} = S_x + S_y - S_p - S_q\]
其中 \(p=\operatorname{LCA}(x,y),q=\operatorname{father}(p)\)。
结合一张图理解一下:
那么询问就很好写了:
int calc(int a,int b,int s,int t,int val)
{//询问要求a->b的路径,s=LCA(a,b),t=father(s),val是询问给定的值
int ret=0;//结果
for(register int i=Bit;~i;i--)//从高到低枚举每一位
{
int c=(val>>i)&1;//当前位
if(cnt[ch[a][!c]]+cnt[ch[b][!c]]>cnt[ch[s][!c]]+cnt[ch[t][!c]])//若真实所求的 Trie 树中含有一条不同于当前位的路径
ret|=(1<<i),c^=1;//加入答案,并重新设置下一个要走的位置
a=ch[a][c],b=ch[b][c],s=ch[s][c],t=ch[t][c];//四个点都要走下去
}
return ret;//返回答案
}
把初始化操作也提一下(LCA略去):
int insert(int pre,int val)
{//上一个版本的根节点为pre,插入值为val
int rt=++total;
int now=rt;//新开根节点,这里不更新cnt没事是因为询问会忽略根。
for(register int i=Bit;~i;i--)//从高到低枚举每一位
{
int c=(val>>i)&1;//当前为
ch[now][c]=++total;//当前位新开结点
ch[now][!c]=ch[pre][!c];//非当前位直接复制上个版本的
now=ch[now][c],pre=ch[pre][c];//走下去
cnt[now]=cnt[pre]+1;//更新cnt值
}
return rt;//别忘了返回新根结点的编号。
}
void Dfs3(int rt,int f)//初始化用Dfs实现(Dfs1,2用来处理树剖lca了)
{
root[rt]=insert(root[f],w[rt]);//在父结点信息的基础上更新上当前结点的权值
for(register int i=0;i<int(G[rt].size());i++)
if(G[rt][i]!=f) Dfs3(G[rt][i],rt);//Dfs下一层
}
最后贴一下完整代码:
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int N=1e5+5;
int n,q,w[N];
vector<int> G[N];
int fa[N],size[N];
int dep[N],maxs[N];
int top[N];
void Dfs1(int rt,int f)
{
fa[rt]=f,dep[rt]=dep[f]+1,size[rt]=1;
for(register int i=0;i<int(G[rt].size());i++)
{
int nxt=G[rt][i];
if(nxt==f) continue;
Dfs1(nxt,rt);
size[rt]+=size[nxt];
if(size[maxs[rt]]<size[nxt]) maxs[rt]=nxt;
}
}
void Dfs2(int rt,int tp)
{
top[rt]=tp;
if(size[rt]>1) Dfs2(maxs[rt],tp);
for(register int i=0;i<int(G[rt].size());i++)
if(G[rt][i]!=fa[rt]&&G[rt][i]!=maxs[rt])
Dfs2(G[rt][i],G[rt][i]);
}
int get_lca(int u,int v)
{
while(top[u]!=top[v])
(dep[top[u]]>=dep[top[v]])?u=fa[top[u]]:v=fa[top[v]];
return (dep[u]<dep[v])?u:v;
}
const int Bit=16;
const int SZ=2e6+5;
int ch[SZ][2];
int cnt[SZ];
int total=1;
int root[N];
int insert(int pre,int val)
{
int rt=++total;
int now=rt;
for(register int i=Bit;~i;i--)
{
int c=(val>>i)&1;
ch[now][c]=++total;
ch[now][!c]=ch[pre][!c];
now=ch[now][c],pre=ch[pre][c];
cnt[now]=cnt[pre]+1;
}
return rt;
}
void Dfs3(int rt,int f)
{
root[rt]=insert(root[f],w[rt]);
for(register int i=0;i<int(G[rt].size());i++)
if(G[rt][i]!=f) Dfs3(G[rt][i],rt);
}
int calc(int a,int b,int s,int t,int val)
{
int ret=0;
for(register int i=Bit;~i;i--)
{
int c=(val>>i)&1;
if(cnt[ch[a][!c]]+cnt[ch[b][!c]]>cnt[ch[s][!c]]+cnt[ch[t][!c]])
ret|=(1<<i),c^=1;
a=ch[a][c],b=ch[b][c],s=ch[s][c],t=ch[t][c];
}
return ret;
}
void solve()
{
for(register int i=1;i<=n;i++)
scanf("%d",w+i),G[i].clear();
for(register int i=1,u,v;i<n;i++)
{
scanf("%d%d",&u,&v);
G[u].push_back(v),G[v].push_back(u);
}
memset(fa,0,sizeof(fa));
memset(size,0,sizeof(size));
memset(dep,0,sizeof(dep));
memset(top,0,sizeof(top));
memset(maxs,0,sizeof(maxs));
Dfs1(1,0);
Dfs2(1,1);
memset(root,0,sizeof(root));
memset(ch,0,sizeof(ch));
memset(cnt,0,sizeof(cnt));
total=1;
Dfs3(1,0);
while(q--)
{
int u,v,k;
scanf("%d%d%d",&u,&v,&k);
int lca=get_lca(u,v),fl=fa[lca];
int ans=calc(root[u],root[v],root[lca],root[fl],k);
printf("%d\n",ans);
}
}
signed main()
{
while(~scanf("%d%d",&n,&q))
solve();
return 0;
}