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<cstdio>
#include<vector>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
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=1e5+10;
int n,m;
int a[maxn],b[maxn];
struct node{int v,nxt;}E[maxn<<1];
int head[maxn],tot;
int fa[maxn],dep[maxn],size[maxn],cnt;
int top[maxn],num[maxn],pos[maxn],son[maxn];
int rt[2][maxn],sz[2],nxt[2][maxn<<5][2],sum[2][maxn<<5];
//这些数组第一维1表示第一个询问的,0表示第二个询问的
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; b[cnt]=a[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 cnt,int p)//可持久化Trie建树
{
int rt=++sz[p]; sum[p][rt]=sum[p][pre]+1;
if(cnt<0) return rt;
int d=x>>cnt&1;
nxt[p][rt][d^1]=nxt[p][pre][d^1];
nxt[p][rt][d]=update(nxt[p][pre][d],x,cnt-1,p);
return rt;
}
void dfs(int u)//深搜建树
{
rt[0][u]=update(rt[0][fa[u]],a[u],30,0);
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 cnt)
{
if(cnt<0) return 0;
int d=x>>cnt&1;
int ss=sum[0][nxt[0][u][d^1]]+sum[0][nxt[0][v][d^1]]-sum[0][nxt[0][lca][d^1]]-sum[0][nxt[0][gra][d^1]];
if(ss>0) return (1<<cnt)+query2(nxt[0][u][d^1],nxt[0][v][d^1],nxt[0][lca][d^1],nxt[0][gra][d^1],x,cnt-1);
else return query2(nxt[0][u][d],nxt[0][v][d],nxt[0][lca][d],nxt[0][gra][d],x,cnt-1);
}
int query1(int u,int v,int x,int cnt)
{
if(cnt<0) return 0;
int d=x>>cnt&1;
int ss=sum[1][nxt[1][v][d^1]]-sum[1][nxt[1][u][d^1]];
if(ss>0) return (1<<cnt)+query1(nxt[1][u][d^1],nxt[1][v][d^1],x,cnt-1);
else return query1(nxt[1][u][d],nxt[1][v][d],x,cnt-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]];
}
if(dep[u]<dep[v]) return u;
else return v;
}
int main()
{
n=read();m=read();
for(int i=1;i<=n;++i) a[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);//树剖
dfs(1);//深搜建树,对于第二个询问
for(int i=1;i<=n;++i)//按树剖编号建树,第一个询问
rt[1][i]=update(rt[1][i-1],b[i],30,1);
while(m--)
{
int k=read();
if(k==1)
{
int u=read(),x=read();
printf("%d\n",query1(rt[1][num[u]-1],rt[1][num[u]+size[u]-1],x,30));
//第num[u]+size[u]-1与第num[u]-1棵树相减
}
else if(k==2)
{
int u=read(),v=read(),x=read();
int lca=LCA(u,v);
printf("%d\n",query2(rt[0][u],rt[0][v],rt[0][lca],rt[0][fa[lca]],x,30));
//差分路径
}
}
return 0;
}