[洛谷P3292] [SCOI2016]幸运数字

P3292 [SCOI2016]幸运数字

一颗带点权的树,每次查询指定两个点,求在两个点之间的这条路径(包括两端)上选任意个数能得到的最大异或值。

做法

考虑倍增维护LCA,同时维护向上(2^k)的点的线性基,然后查询求LCA时将经过的所有线性基暴力合并即可。

#include <cmath>
#include <cstdio>
#include <cstring>
template <class T>
inline void read(T &num)
{
    bool flag = 0;
    num = 0;
    char c = getchar();
    while ((c < '0' || c > '9') && c != '-')
        c = getchar();
    if (c == '-')
    {
        flag = 1;
        c = getchar();
    }
    num = c - '0';
    c = getchar();
    while (c >= '0' && c <= '9')
        num = (num << 3) + (num << 1) + c - '0', c = getchar();
    if (flag)
        num *= -1;
}
template <class T>
inline void output(T num)
{
    if (num < 0)
    {
        putchar('-');
        num = -num;
    }
    if (num >= 10)
        output(num / 10);
    putchar(num % 10 + '0');
}
template <class T>
inline void outln(T num)
{
    output(num);
    putchar('
');
}
template <class T>
inline void outps(T num)
{
    output(num);
    putchar(' ');
}
template <class T>
inline T max(T a, T b)
{
    return a < b ? b : a;
}
typedef long long ll;
struct basis // 线性基
{
    typedef ll type;
    static const int W = 62;
    type s[W];
    basis()
    {
        memset(s, 0, sizeof(s));
    }
    void ins(type x) // 向线性基内插入一个数
    {
        for (int i = W; i >= 1; i--)
        {
            if (x >> (i - 1))
            {
                if (s[i] == 0)
                {
                    s[i] = x;
                    return;
                }
                x ^= s[i];
            }
        }
    }
    type maxxor() // 求线性基内最大xor值
    {
        type ans = 0;
        for (int i = W; i >= 1; i--)
        {
            ans = max(ans, ans ^ s[i]);
        }
        return ans;
    }
};
const int N = 20005;
const int lN = 20;
int n, q, ln;
namespace graph // 存图
{
    int fir[N], to[N * 2], nxt[N * 2], ecnt;
    void add_edge(int u, int v)
    {
        to[++ecnt] = v;
        nxt[ecnt] = fir[u];
        fir[u] = ecnt;
    }
} // namespace graph
namespace tree
{
    ll g[N];                  // g[i]     : i号点的数字
    int dep[N];               // dep[i]   : i号点的深度
    int fa[N][lN];            // fa[i][j] : i号点向上跳2^j到达的节点
    basis b[N][lN];           // b[i][j]  : i号点以上的2^j点的线性基(显然不包括fa[i][j])
    void dfs(int node, int f) // 深搜初始化
    {
        using namespace graph;
        dep[node] = dep[f] + 1;
        fa[node][0] = f;
        b[node][0].ins(g[node]);
        for (int i = 1; i <= ln; i++) // 倍增求2^k次祖先以及路径上数字的线性基
        {
            fa[node][i] = fa[fa[node][i - 1]][i - 1];
            if (!fa[node][i])
                break;
            for (int j = 1; j <= basis::W; j++)
            {
                if (b[node][i - 1].s[j])
                    b[node][i].ins(b[node][i - 1].s[j]);
                if (b[fa[node][i - 1]][i - 1].s[j])
                    b[node][i].ins(b[fa[node][i - 1]][i - 1].s[j]);
            }
        }
        for (int e = fir[node]; e; e = nxt[e])
        {
            if (to[e] == f)
                continue;
            dfs(to[e], node);
        }
    }
} // namespace tree
ll solve(int x, int y)
{
    using namespace tree;
    basis rtn;
    if (dep[x] < dep[y])
        x ^= y ^= x ^= y;
    for (int k = ln; k >= 0; k--)
        if (dep[fa[x][k]] >= dep[y])
        {
            for (int j = 1; j <= basis::W; j++)
                if (b[x][k].s[j])
                    rtn.ins(b[x][k].s[j]);
            x = fa[x][k];
        }
    if (x == y)
    {
        rtn.ins(g[x]);
        return rtn.maxxor();
    }
    for (int i = ln; i >= 0; i--)
    {
        if (dep[fa[x][i]] == 0)
            continue;
        if (fa[x][i] == fa[y][i])
            continue;
        for (int j = 1; j <= basis::W; j++)
        {
            if (b[x][i].s[j])
                rtn.ins(b[x][i].s[j]);
            if (b[y][i].s[j])
                rtn.ins(b[y][i].s[j]);
        }
        x = fa[x][i];
        y = fa[y][i];
    }
    rtn.ins(g[x]);
    rtn.ins(g[y]);
    rtn.ins(g[fa[x][0]]);
    return rtn.maxxor();
}
int main()
{
    read(n);
    read(q);
    ln = ceil(log(n) / log(2)) + 2;
    for (int i = 1; i <= n; i++)
        read(tree::g[i]);
    for (int i = 1; i < n; i++)
    {
        int x, y;
        read(x);
        read(y);
        graph::add_edge(x, y);
        graph::add_edge(y, x);
    }
    tree::dfs(1, 0);
    while (q--)
    {
        int x, y;
        read(x);
        read(y);
        outln(solve(x, y));
    }
}
原文地址:https://www.cnblogs.com/water-lift/p/11166488.html