P5556 圣剑护符 题解

对一次询问,假设已经建出了这条树链的线性基,考虑存在两个子集异或和相等的条件。

若存在未成功插入线性基的数 $k$,则一定可以在线性基中选出异或和为 $k$ 的子集 $S$,此时存在 $\{k\}$ 与 $S$ 异或和相等。

否则,树链的 $2^n$ 个子集异或和对应线性基的 $2^n$ 个互不相同的子集异或和,此时不存在两个子集异或和相等。

综上所述,存在两个子集异或和相等当且仅当存在未成功插入线性基的数。

线性基最多成功插入 $\log V$ 个数,所以长度大于 $\log V$ 的树链上一定存在未成功插入线性基的数。

对于长度小于等于 $\log V$ 的树链,直接建出线性基判断是否存在未成功插入线性基的数即可。

现在需要维护树链异或,单点查询,BIT 即可。

#include <cstdio>
#include <algorithm>
using namespace std;
struct E
{
    int v, t;
} e[200050];
char o[10];
int n, m, c, p, P[50], a[100050], b[100050], d[100050], s[100050], C[100050], h[100050], f[100050][20];
void A(int u, int v)
{
    e[++c] = {v, h[u]};
    h[u] = c;
}
void M(int x, int k)
{
    for (; x <= n; x += x & -x)
        C[x] ^= k;
}
int Q(int x)
{
    int q = 0;
    for (; x; x &= x - 1)
        q ^= C[x];
    return q;
}
void D(int u, int k)
{
    s[u] = 1;
    b[u] = ++p;
    for (int i = h[u], v; i; i = e[i].t)
        if ((v = e[i].v) != k)
        {
            d[v] = d[f[v][0] = u] + 1;
            for (int j = 1; f[v][j - 1]; ++j)
                f[v][j] = f[f[v][j - 1]][j - 1];
            D(v, u);
            s[u] += s[v];
        }
}
int L(int x, int y)
{
    if (d[x] < d[y])
        swap(x, y);
    while (d[x] > d[y])
        x = f[x][__lg(d[x] - d[y])];
    if (x == y)
        return x;
    for (int k = __lg(d[x]); k >= 0; --k)
        if (f[x][k] != f[y][k])
            x = f[x][k], y = f[y][k];
    return f[x][0];
}
bool I(int x)
{
    for (int i = 30; i >= 0; --i)
        if (x >> i & 1)
        {
            if (!P[i])
                return P[i] = x, 1;
            else
                x ^= P[i];
        }
    return 0;
}
int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; ++i)
        scanf("%d", a + i);
    for (int i = 1, u, v; i < n; ++i)
        scanf("%d%d", &u, &v), A(u, v), A(v, u);
    D(d[1] = 1, 0);
    for (int i = 1; i <= n; ++i)
    {
        M(b[i], a[i]);
        if (f[i][0])
            M(b[f[i][0]], a[i]);
    }
    for (int i = 0, x, y, k, l; i < m; ++i)
    {
        scanf("%s%d%d", o, &x, &y);
        if (o[0] == 'U')
        {
            scanf("%d", &k), M(b[x], k), M(b[y], k), M(b[l = L(x, y)], k);
            if (f[l][0])
                M(b[f[l][0]], k);
        }
        else
        {
            if (d[x] + d[y] - (d[L(x, y)] << 1) >= 30)
            {
                puts("YES");
                continue;
            }
            for (int i = 0; i <= 30; ++i)
                P[i] = 0;
            if (d[x] < d[y])
                swap(x, y);
            bool F = 0;
            while (d[x] > d[y])
            {
                if (!I(Q(b[x] + s[x] - 1) ^ Q(b[x] - 1)))
                {
                    F = 1;
                    break;
                }
                x = f[x][0];
            }
            if (F)
            {
                puts("YES");
                continue;
            }
            while (x != y)
            {
                if (!I(Q(b[x] + s[x] - 1) ^ Q(b[x] - 1)))
                {
                    F = 1;
                    break;
                }
                if (!I(Q(b[y] + s[y] - 1) ^ Q(b[y] - 1)))
                {
                    F = 1;
                    break;
                }
                x = f[x][0];
                y = f[y][0];
            }
            if (!I(Q(b[x] + s[x] - 1) ^ Q(b[x] - 1)))
                F = 1;
            if (F)
            {
                puts("YES");
                continue;
            }
            puts("NO");
        }
    }
    return 0;
}
posted @ 2023-12-19 10:28  5k_sync_closer  阅读(23)  评论(0编辑  收藏  举报  来源