题目大意:
就是现在给出一棵树, 结点个数不超过10W, 每个节点上有一个不超过2^16的非负整数, 然后10W次询问, 每次询问两个节点的路径上的所有数中异或上给出的数的最大值
大致思路:
刚开始做这个题想的是树链剖分之后用线段树套Trie树来做…结果悲剧地MLE了…
另外那样做得话复杂度其实是比较大的…每次询问都是16*(logn)*logn级别的..
后来发现是个可持久化Trie树…
表示第一次写可持久化Trie树, 感觉和主席树很像, 都是多个版本的Trie的集合体, 然后由要访问的版本的不同而从不同的根节点出发来访问
对于给出的树按照父亲和儿子的关系来建立可持久化Trie树
每次访问的时候访问到的Trie树版本是考虑了从当前结点到父亲结点的路径上的所有点的一颗Trie树, 那么对于每次的路径(u, v)的查询, 就是u开始的Trie与v开始的Trie的查询减去他们的LCA的查询, 然后利用贪心的思想求出可能的最大值即可
代码如下:
Result : Accepted Memory : 41812 KB Time : 1528 ms
/*
* Author: Gatevin
* Created Time: 2015/10/2 15:20:38
* File Name: Sakura_Chiyo.cpp
*/
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
using namespace std;
const double eps(1e-8);
typedef long long lint;
#define maxn 100010
struct Edge
{
int u, v, nex;
Edge(int _u, int _v, int _nex)
{
u = _u, v = _v, nex = _nex;
}
Edge(){}
};
Edge edge[maxn << 1];
int head[maxn];
int tot;
void add_Edge(int u, int v)
{
edge[++tot] = Edge(u, v, head[u]);
head[u] = tot;
}
int w[maxn];
int father[maxn][20];
int dep[maxn];
int n, m;
/*
* 可持久化Trie树
*/
struct Trie
{
#define maxnode 2800100
int next[maxnode][2];
int L;
int root[maxn];
int sz[maxnode];
void init()
{
L = 0;
memset(root, 0, sizeof(root));
memset(sz, 0, sizeof(sz));
}
int newnode()
{
next[L][0] = next[L][1] = -1;
L++;
return L - 1;
}
/*
* 一个以root[x]开始的Trie树是树上x向上到链的数组成的Trie树
* 然后树上每个节点都用sz[u][v]表示以u向上到根的值组成的Trie树中结点v代表的出现次数
*/
void insert(int x, int fa, int value)//x的父亲节点是y
{
x = root[x];
int y = root[fa];
for(int i = 15; i >= 0; i--)
{
int nex = 0;
if(value & (1 << i)) nex = 1;
if(next[x][nex] == -1)
{
int id = newnode();
next[x][nex] = id;
next[x][nex^1] = next[y][nex^1];//合并
sz[next[x][nex]] = sz[next[y][nex]];
}
x = next[x][nex], y = next[y][nex];
++sz[x];
}
}
int query(int x, int y, int z, int value)//z是x和y的lca
{
int ret = 0, ano = w[z];
x = root[x], y = root[y], z = root[z];
for(int i = 15; i >= 0; i--)
{
int nex = 0;
if(value & (1 << i)) nex = 1;
if(sz[next[x][nex ^ 1]] + sz[next[y][nex ^ 1]] - 2*sz[next[z][nex ^ 1]] > 0)//说明在[x, y]的链上有数提供这样的路走
{
x = next[x][nex ^ 1], y = next[y][nex ^ 1], z = next[z][nex ^ 1];
ret += (1 << i);
}
else
{
x = next[x][nex], y = next[y][nex], z = next[z][nex];
}
}
return max(ret, ano ^ value);
}
};
Trie trie;
void dfs(int now)
{
trie.root[now] = trie.newnode();
trie.insert(now, father[now][0], w[now]);//按照树的列的顺序插入
for(int i = head[now]; i + 1; i = edge[i].nex)
{
if(edge[i].v == father[now][0]) continue;
father[edge[i].v][0] = now;
dep[edge[i].v] = dep[now] + 1;
dfs(edge[i].v);
}
return;
}
int swim(int x, int H)
{
for(int i = 0; H > 0; i++)
{
if(H & 1) x = father[x][i];
H >>= 1;
}
return x;
}
int lca(int x, int y)
{
int i;
if(dep[x] < dep[y]) swap(x, y);
int k = dep[x] - dep[y];
x = swim(x, k);
if(x == y) return x;
while(1)
{
for(i = 0; father[x][i] != father[y][i]; i++);
if(i == 0) return father[x][0];
x = father[x][i - 1];
y = father[y][i - 1];
}
return -1;
}
void answer(int x, int y, int z)
{
printf("%d\n", trie.query(x, y, lca(x, y), z));
}
bool get(int& x)
{
x = 0;
char c;
bool ined = 0;
while(!ined)
{
c = getchar();
if(c == EOF) break;
while(c >= '0' && c <= '9')
{
ined = 1;
x = (x << 3) + (x << 1) + c - '0';
c = getchar();
}
}
return ined;
}
int main()
{
while(get(n))
{
get(m);
memset(head, -1, sizeof(head));
tot = 0;
for(int i = 1; i <= n; i++)
get(w[i]);
int u, v;
for(int i = 1; i < n; i++)
{
get(u); get(v);
add_Edge(u, v);
add_Edge(v, u);
}
trie.init();
memset(father, 0, sizeof(father));
dep[1] = 1;
father[1][0] = -1;
dfs(1);
for(int j = 1; (1 << j) <= n; j++)
for(int i = 1; i <= n; i++)
if(father[i][j - 1] != -1)
father[i][j] = father[father[i][j - 1]][j - 1];//准备好求lca
while(m--)
{
int x, y, z;
scanf("%d %d %d", &x, &y, &z);
answer(x, y, z);
}
}
return 0;
}