题解[TJOI2018]异或
对于题目给出的两个查询,我们我可以将它这样转换:
对于操作一,用dfs序将一个子树转化为一段连续的区间,然后根据dfs序建立一个trie树,那么查询一个子树即为查询一段区间
对于操作二,根据根到结点的路径建立一个trie树,同样也可以转化为一个区间查询
于是就可以建两个trie树,分别对应两个操作
结合代码理解
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N = 4e5 + 50, M = 32 * N;
int tr1[M][2], root1[N], tot1, siz[N];
int w[N], n, q, maxn[M], id[N], it[N];
int head[N], ver[N], net[N], idx, cnt, root2[N];
int tr2[M][2], dep[N], fa[N][25], maxd[M], tot2;
void add(int a, int b)
{
net[idx] = head[a];
ver[idx] = b;
head[a] = idx++;
}
void insert1(int p, int q, int i, int k)
{
if (i < 0)
{
maxn[p] = k;//记录一下最大dfs序
return;
}
int v = w[it[k]] >> i & 1;
if (q)
tr1[p][!v] = tr1[q][!v];
tr1[p][v] = ++tot1;
insert1(tr1[p][v], tr1[q][v], i - 1, k);
maxn[p] = max(maxn[tr1[p][0]], maxn[tr1[p][1]]);
return;
}
void insert2(int p, int q, int i, int k)
{
if (i < 0)
{
maxd[p] = dep[k];//记录一下最大深度
return;
}
int v = w[k] >> i & 1;
if (q)
tr2[p][!v] = tr2[q][!v];
tr2[p][v] = ++tot2;
insert2(tr2[p][v], tr2[q][v], i - 1, k);
maxd[p] = max(maxd[tr2[p][0]], maxd[tr2[p][1]]);
return;
}
int query1(int p, int v, int l)
{
for (int i = 30; ~i; --i)
{
int s = v >> i & 1;
if (maxn[tr1[p][!s]] >= l)
p = tr1[p][!s];
else
p = tr1[p][s];
}
return v ^ w[it[maxn[p]]];
}
int query2(int p, int v, int l)
{
int res = 0;
for (int i = 30; ~i; --i)
{
int s = v >> i & 1;
if (maxd[tr2[p][!s]] >= l)
p = tr2[p][!s], res += 1 << i;
else
p = tr2[p][s];
}
return res;
}
void dfs(int u, int f)
{
id[u] = ++cnt, it[cnt] = u;
siz[u] = 1, dep[u] = dep[f] + 1, fa[u][0] = f;
for (int i = 1; i <= 20; i++)
fa[u][i] = fa[fa[u][i - 1]][i - 1];
root2[u] = ++tot2;
insert2(root2[u], root2[f], 30, u);//按照到根节点的路径建树
for (int i = head[u]; ~i; i = net[i])
{
int v = ver[i];
if (v == f)
continue;
dfs(v, u);
siz[u] += siz[v];
}
}
int lca(int x, int y)
{
if (dep[x] > dep[y])
swap(x, y);
for (int i = 20; ~i; --i)
if (dep[fa[y][i]] >= dep[x])
y = fa[y][i];
if (x == y)
return x;
for (int i = 20; ~i; --i)
if (fa[x][i] != fa[y][i])
x = fa[x][i], y = fa[y][i];
return fa[x][0];
}
int main()
{
memset(head, -1, sizeof(head));
scanf("%d%d", &n, &q);
for (int i = 1; i <= n; i++)
scanf("%d", &w[i]);
for (int i = 1; i < n; i++)
{
int u, v;
scanf("%d%d", &u, &v);
add(u, v), add(v, u);
}
dfs(1, 0);
for (int i = 1; i <= n; i++)
{
root1[i] = ++tot1;
insert1(root1[i], root1[i - 1], 30, i);
}
while (q--)
{
int op, x, y, z;
scanf("%d", &op);
if (op == 1)
{
scanf("%d%d", &x, &z);
printf("%d\n", query1(root1[id[x] + siz[x] - 1], z, id[x]));//查找x的子树,
}
else
{
scanf("%d%d%d", &x, &y, &z);
int u = lca(x, y);
printf("%d\n", max(query2(root2[x], z, dep[u]), query2(root2[y], z, dep[u])));
}
}
return 0;
}