洛谷 P4592 / LOJ 2577「TJOI2018」异或
思路
显然可持久化 01 trie。
询问一,由于一个结点的子树可以映射到 \(\mathrm{dfs}\) 序上一段连续的区间,因此可以对 \(\mathrm{dfn}\) 区间建可持久化 01 trie,查询直接做就可以。
询问二,用树剖会多一个 \(\log\),很垃圾。考虑差分思想,\(x\) 到 \(y\) 的简单路径相当于 \(x\) 到根结点的路径加上 \(y\) 到根结点的路径,减去 \(\mathrm{lca}(x,y)\) 到根结点的路径再减去 \(fa_{\mathrm{lca}(x,y)}\) 到根结点的路径。对每个结点到根结点的链建可持久化 01 trie,查询时差分即可。
时间复杂度 \(O(n \log w)\)。
代码
code
/*
p_b_p_b txdy
AThousandSuns txdy
Wu_Ren txdy
Appleblue17 txdy
*/
#include <bits/stdc++.h>
#define pb push_back
#define fst first
#define scd second
#define mems(a, x) memset((a), (x), sizeof(a))
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<ll, ll> pii;
const int maxn = 100100;
const int maxm = 10000000;
int n, m, a[maxn], head[maxn], len;
int rt1[maxn], rt2[maxn], ch[maxm][2], tsz[maxm], totn;
int dp[maxn], sz[maxn], son[maxn], fa[maxn];
int top[maxn], st[maxn], times, ed[maxn];
bool vis[maxn];
struct edge {
int to, next;
} edges[maxn << 1];
void add_edge(int u, int v) {
edges[++len].to = v;
edges[len].next = head[u];
head[u] = len;
}
int insert(int rt, int x, int d) {
int p = ++totn;
ch[p][0] = ch[rt][0];
ch[p][1] = ch[rt][1];
tsz[p] = tsz[rt] + 1;
if (d >= 0) {
int k = ((x >> d) & 1);
ch[p][k] = insert(ch[p][k], x, d - 1);
}
return p;
}
int query(int p, int q, int x, int d) {
if (d == -1) {
return 0;
}
int k = ((x >> d) & 1);
if (tsz[ch[q][k ^ 1]] - tsz[ch[p][k ^ 1]] > 0) {
return (1 << d) | query(ch[p][k ^ 1], ch[q][k ^ 1], x, d - 1);
} else {
return query(ch[p][k], ch[q][k], x, d - 1);
}
}
int dfs(int u, int f, int d) {
dp[u] = d;
sz[u] = 1;
fa[u] = f;
st[u] = ++times;
rt1[times] = insert(rt1[times - 1], a[u], 29);
int maxson = -1;
for (int i = head[u]; i; i = edges[i].next) {
int v = edges[i].to;
if (v == f) {
continue;
}
rt2[v] = insert(rt2[u], a[v], 29);
sz[u] += dfs(v, u, d + 1);
if (sz[v] > maxson) {
son[u] = v;
maxson = sz[v];
}
}
ed[u] = times;
return sz[u];
}
void dfs2(int u, int tp) {
top[u] = tp;
vis[u] = 1;
if (!son[u]) {
return;
}
dfs2(son[u], tp);
for (int i = head[u]; i; i = edges[i].next) {
int v = edges[i].to;
if (!vis[v]) {
dfs2(v, v);
}
}
}
int querylca(int x, int y) {
while (top[x] != top[y]) {
if (dp[top[x]] < dp[top[y]]) {
swap(x, y);
}
x = fa[top[x]];
}
if (dp[x] > dp[y]) {
swap(x, y);
}
return x;
}
void solve() {
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);
add_edge(u, v);
add_edge(v, u);
}
dfs(1, 0, 1);
dfs2(1, 1);
while (m--) {
int op, x, y, z;
scanf("%d%d%d", &op, &x, &y);
if (op == 1) {
printf("%d\n", query(rt1[st[x] - 1], rt1[ed[x]], y, 29));
} else {
scanf("%d", &z);
int lca = querylca(x, y);
int k = fa[lca];
printf("%d\n", max(query(rt2[k], rt2[x], z, 29), query(rt2[lca], rt2[y], z, 29)));
}
}
}
int main() {
int T = 1;
// scanf("%d", &T);
while (T--) {
solve();
}
return 0;
}