Problem Description
Zero and One are good friends who always have fun with each other. This time, they decide to do something on a tree which is a kind of graph that there is only one path from node to node. First, Zero will give One an tree and every node in this tree has a value. Then, Zero will ask One a series of queries. Each query contains three parameters: x, y, z which mean that he want to know the maximum value produced by z xor each value on the path from node x to node y (include node x, node y). Unfortunately, One has no idea in this question. So he need you to solve it.
Input
There are several test cases and the cases end with EOF. For each case:
The first line contains two integers n(1<=n<=105) n ( 1 <= n <= 10 5 ) and m(1<=m<=105) m ( 1 <= m <= 10 5 ) , which are the amount of tree’s nodes and queries, respectively.
The second line contains n integers a[1..n] a [ 1.. n ] and a[i](0<=a[i]<216) a [ i ] ( 0 <= a [ i ] < 2 16 ) is the value on the ith node.
The next n–1 lines contains two integers u v, which means there is an connection between u and v.
The next m lines contains three integers x y z, which are the parameters of Zero’s query.
Output
For each query, output the answer.
Sample Input
3 2
1 2 2
1 2
2 3
1 3 1
2 3 2
Sample Output
3
0
题意:
给一棵树,每个节点有权值。每次询问要求回答一个值XOR某条路径上的一个点的最大值。
思路:
曾经学过主席树,可以知道所谓可持久化就是可以保留历史记录,然后再历史记录之上修改得到当前的数据,在查询某个区间的数据时,只要利用前缀和的思想,头尾相减就行了。
那既然线段树是可以可持久化的,字典树肯定也是可以的。这道题要求的区间是树上的一条路径,那么很容易想到用倍增求LCA,然后分成左端点到LCA的路和右端点到LCA的路来做。所以我们可以对每一个点开一个字典树,记录从这个点到根的路径上的所有数,顺便在字典树节点上记一个num,表示经过这个节点的数有多少个,这样就可以用前缀和了。
实现不是很烦(然而我求LCA打错了,还一直没看出来)
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N = 1e5+10;
const int E = 20;
const int B = 18;
int n, m;
struct EDGE{
int nxt, v;
}edge[N<<1];
int a[N], point[N], e, dpt[N], f[N][E+1];
struct TRIE{
int son[2], num;
void Clear(){
son[0] = son[1] = num = 0;
}
}trie[N*60];
int root[N], tot;
void add_edge(int u, int v)
{
edge[++e] = (EDGE){point[u], v};
point[u] = e;
}
int Newnode()
{
tot++;
trie[tot].Clear();
return tot;
}
void Build(int &u, int dpt)
{
u = Newnode();
if (dpt == B) return;
for (int i = 0; i < 2; i++)
Build(trie[u].son[i], dpt+1);
}
void Update(int pre, int now, int s)
{
int val;
for (int i = B; i >= 0; i--){
val = ((s>>i)&1);
trie[now].son[val^1] = trie[pre].son[val^1];
trie[now].son[val] = Newnode();
now = trie[now].son[val];
pre = trie[pre].son[val];
trie[now].num = trie[pre].num+1;
}
}
void Dfs(int u, int fa)
{
root[u] = ++tot, trie[tot].Clear();
Update(root[fa], root[u], a[u]);
dpt[u] = dpt[fa]+1;
f[u][0] = fa;
for (int i = 1; i <= E; i++)
f[u][i] = f[f[u][i-1]][i-1];
for (int i = point[u]; i != -1; i = edge[i].nxt){
int v = edge[i].v;
if (v != fa) Dfs(v, u);
}
}
int Lca(int x, int y)
{
if (dpt[x] < dpt[y]) swap(x, y);
for (int i = E; i >= 0; i--)
if (dpt[x]-(1<<i) >= dpt[y])
x = f[x][i];
if (x == y) return x;
for (int i = E; i >= 0; i--)
if (f[x][i] != f[y][i])
x = f[x][i], y = f[y][i];
if (x != y) x = f[x][0];
return x;
}
int Search(int pre, int now, int s)
{
int val, ret = 0;
for (int i = B; i >= 0; i--){
val = ((s>>i)&1);
if (trie[trie[now].son[val^1]].num-trie[trie[pre].son[val^1]].num > 0)
ret += (1<<i), now = trie[now].son[val^1], pre = trie[pre].son[val^1];
else
now = trie[now].son[val], pre = trie[pre].son[val];
}
return ret;
}
int main()
{
while (scanf("%d%d", &n, &m) == 2){
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
memset(point, -1, sizeof(point)); e = 0;
for (int i = 1; i < n; i++){
int x, y;
scanf("%d%d", &x, &y);
add_edge(x, y);
add_edge(y, x);
}
tot = 0; dpt[0] = 0;
root[0] = Newnode();
memset(f, 0, sizeof(f));
Dfs(1, 0);
for (int i = 1; i <= m; i++){
int x, y, z, fa;
scanf("%d%d%d", &x, &y, &z);
fa = f[Lca(x, y)][0];
printf("%d\n", max(Search(root[fa], root[x], z), Search(root[fa], root[y], z)));
}
}
return 0;
}