Time Limit: 30 Sec
Memory Limit: 256 MB
Description
现在有一颗以1为根节点的由n个节点组成的树,树上每个节点上都有一个权值vi。
现在有Q 次操作,操作如下:
1 x y 查询节点x的子树中与y异或结果的最大值
2 x y z 查询路径x到y上点与z异或结果最大值
Input
第一行是两个数字n, Q;
第二行是n个数字用空格隔开,第i个数字vi表示点i上的权值
接下来n-1行,每行两个数,x,y,表示节点x与y之间有边
接下来Q行,每一行为一个查询,格式如上所述.
1 < n, Q ≤ 100000 ,查询1中的y ≤ 2^30 ,查询2中的z ≤ 2^30
Output
对于每一个查询,输出一行,表示满足条件的最大值。
题目分析
区间异或最大值,马上想到可持久化Trie
但是这两个操作要怎么建到一棵树啊啊啊啊
深思熟虑了好久发现还是得建两棵树
对于1询问,对原树树剖一下
利用树剖编号建立可持久化Trie
即维护按编号顺序的前缀
对于询问2
每个结点建可持久化Trie维护该点到根的路径
u~v的路径利用差分
u的Trie+v的Trie-lca(u,v)的Trie-fa[lca(u,v)]的Trie
由于建了两棵可持久化Trie
代码有些乱=_=
#include<iostream>
#include<vector>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long lt;
int read()
{
int f=1,x=0;
char ss=getchar();
while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
return f*x;
}
const int maxn=500010;
int n,m;
struct node{int v,nxt;}E[maxn<<1];
int head[maxn],tot;
int val[maxn],size[maxn],top[maxn],dep[maxn];
int fa[maxn],son[maxn],num[maxn],pos[maxn],cnt;
int rt[2][maxn<<4],sz[2],sum[2][maxn<<4],nxt[2][maxn<<4][2];
void add(int u,int v)
{
E[++tot].nxt=head[u];
E[tot].v=v;
head[u]=tot;
}
void dfs1(int u,int pa)
{
size[u]=1;
for(int i=head[u];i;i=E[i].nxt)
{
int v=E[i].v;
if(v==pa) continue;
dep[v]=dep[u]+1; fa[v]=u;
dfs1(v,u);
size[u]+=size[v];
if(size[v]>size[son[u]]) son[u]=v;
}
}
void dfs2(int u,int tp)
{
top[u]=tp; num[u]=++cnt; pos[cnt]=u;
if(son[u]) dfs2(son[u],tp);
for(int i=head[u];i;i=E[i].nxt)
{
int v=E[i].v;
if(v==fa[u]||v==son[u]) continue;
dfs2(v,v);
}
}
int update(int pre,int x,int cntt,int p)
{
int rt=++sz[p]; sum[p][rt]=sum[p][pre]+1;
if(cntt<0) return rt;
int d=x>>cntt&1;
nxt[p][rt][d^1]=nxt[p][pre][d^1];
nxt[p][rt][d]=update(nxt[p][pre][d],x,cntt-1,p);
return rt;
}
int query1(int u,int v,int x,int cntt)
{
if(cntt<0) return 0;
int d=x>>cntt&1;
int ss=sum[0][nxt[0][v][d^1]]-sum[0][nxt[0][u][d^1]];
if(ss>0) return (1<<cntt)+query1(nxt[0][u][d^1],nxt[0][v][d^1],x,cntt-1);
else return query1(nxt[0][u][d],nxt[0][v][d],x,cntt-1);
}
int LCA(int u,int v)
{
while(top[u]!=top[v])
{
if(dep[top[u]]>dep[top[v]]) u=fa[top[u]];
else v=fa[top[v]];
}
return dep[u]<dep[v]?u:v;
}
void dfs(int u)
{
rt[1][u]=update(rt[1][fa[u]],val[u],30,1);
for(int i=head[u];i;i=E[i].nxt)
if(E[i].v!=fa[u]) dfs(E[i].v);
}
int query2(int u,int v,int lca,int gra,int x,int cntt)
{
if(cntt<0) return 0;
int d=x>>cntt&1;
int lcu=nxt[1][u][d^1],lcv=nxt[1][v][d^1],lc=nxt[1][lca][d^1],lcg=nxt[1][gra][d^1];
int ss=sum[1][lcu]+sum[1][lcv]-sum[1][lc]-sum[1][lcg];
if(ss>0) return (1<<cntt)+query2(lcu,lcv,lc,lcg,x,cntt-1);
else return query2(nxt[1][u][d],nxt[1][v][d],nxt[1][lca][d],nxt[1][gra][d],x,cntt-1);
}
int main()
{
n=read();m=read();
for(int i=1;i<=n;++i) val[i]=read();
for(int i=1;i<n;++i)
{
int u=read(),v=read();
add(u,v); add(v,u);
}
dfs1(1,0); dfs2(1,1);//树剖建树
for(int i=1;i<=n;++i)
rt[0][i]=update(rt[0][i-1],val[pos[i]],30,0);
dfs(1);//深搜建树
while(m--)
{
int opt=read(),x=read(),y=read();
if(opt==1) printf("%d\n",query1(rt[0][num[x]-1],rt[0][num[x]+size[x]-1],y,30));
else if(opt==2)
{
int z=read(),lca=LCA(x,y);
printf("%d\n",query2(rt[1][x],rt[1][y],rt[1][lca],rt[1][fa[lca]],z,30));
}
}
return 0;
}
本文介绍了一种使用树剖和两棵可持久化Trie来解决特定类型的区间异或最大值查询问题的方法。具体包括如何通过树剖进行节点编号,如何构建可持久化Trie来维护子树和路径上的信息,以及如何处理两种不同类型的查询。

336

被折叠的 条评论
为什么被折叠?



