题解[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;
}
posted @ 2021-03-01 10:32  DSHUAIB  阅读(91)  评论(0)    收藏  举报