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;
}