hdu4757

题意

给出一棵树,每个节点有权值,每次查询节点 \((u, v)\) 以及 \(x\) ,问 \(u\) 到 \(v\) 路径上的某个节点与 \(x\) 异或最大的值是多少。

分析

Trie 的新姿势!

如果直接问 \(x\) 与某个区间中哪个数异或后最大,那么直接把区间所有数转化成二进制数(从高到低位,保证长度相同,前面不足补 \(0\) )插入到字典树中,然后查询将每一位都取反的 \(x\) ,比如 \(x\) 为 \(0101\) ,那么我们去查询 \(1010\) ,如果存在对应的位 \(i\),加上 \(1 << i\) 即可。

但是现在有多个查询,怎么优化呢?

对于每一节点都建立一颗 \(Trie\) 树,用一个数组 \(sz\) 记录前缀的数量,如果 \(v\) 是 \(u\) 的子节点,比如 \(v\) 的权值是 \(010\) ,\(u\) 的权值是 \(011\) ,假设 \(u\) 已经加入,那么在加入 \(v\) 的时候,发现 \(01\) 这个前缀数量已经为 \(1\) 了,正好 \(v\) 也有一个这样的前缀,所以 \(sz[now]+=1\) ,也就是说继承了父节点那个 \(Trie\) 对应的前缀的数量,且加上自己的一个。到 \(010\) 时,发现存在 \(011\) 这个前缀,除了新增 \(010\) 这个前缀,对于 \(011\) 我们直接指向父节点的这个对应的值。也就是说我们只更新我们新加入的数对应的那些前缀的数量,其它的全部和父亲节点的 \(Trie\) 树保持一致。也就是每个节点记录的前缀的数量是从当前节点到根节点的所有相应前缀的数量。

再回到上面的做法,当我们去查询每一位都取反的 \(x\) 时,我们其实只关心是否某一位有对应的存在。

如果 \(x\) 为 \(1001\) ,取反后为 \(0110\) ,那么我们其实是想知道 \(u\) 到 \(v\) 这条路径是否有某个前缀为 \(0\) 的数,设 \(k = LCA(u, v)\) , 设 \(F()\) 为到根节点前缀为 \(0\) 的数量,那么计算判断有没有只要 \(F(u) + F(v) - 2 * F(k) > 0\) 即可。(如果这一步取到前缀 \(0\) 了,后面去查找 \(01\) 这个前缀是否存在,如果没有取到,那么取的只能是 \(1\) ,那么就去查找 \(11\) 是否存在)

还有注意这样算 \(k\) 这个节点的权值 \(a[k]\) 是没有算过的,所以最后最后要和 \(a[k] \ xor \ x\) 比较一下取得最大值。

code

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e6 + 10;
const int LOGN = 20;
int n, m;
int a[MAXN];
vector<int> G[MAXN];
int dep[MAXN];
int p[MAXN][LOGN];
void init() {
for(int i = 1; i < LOGN; i++) {
for(int j = 1; j <= n; j++) {
p[j][i] = p[p[j][i - 1]][i - 1];
}
}
}
int lca(int u, int v) {
if(dep[u] > dep[v]) swap(u, v);
for(int i = 0; i < LOGN; i++) {
if((dep[v] - dep[u]) >> i & 1) {
v = p[v][i];
}
}
if(v == u) return u;
for(int i = LOGN - 1; i >= 0; i--) {
if(p[u][i] != p[v][i]) {
u = p[u][i];
v = p[v][i];
}
}
return p[u][0];
}
int sz[MAXN];
int nxt[MAXN][2], root[MAXN], L;
int newnode() {
nxt[L][0] = nxt[L][1] = 0;
return L++;
}
void insert(int u, int fa, int x) {
int now1 = root[u], now2 = root[fa];
for(int i = 18; i >= 0; i--) {
int d = (x >> i) & 1;
nxt[now1][d] = newnode();
nxt[now1][!d] = nxt[now2][!d];
now1 = nxt[now1][d]; now2 = nxt[now2][d];
sz[now1] = sz[now2] + 1;
}
}
int query(int u, int v, int x) {
int k = lca(u, v);
int res = 0;
int now1 = root[u], now2 = root[v], now3 = root[k];
for(int i = 18; i >= 0; i--) {
int d = (x >> i) & 1;
if(nxt[now1][!d] + nxt[now2][!d] - 2 * nxt[now3][!d] > 0) {
res += (1 << i);
d = !d;
}
now1 = nxt[now1][d];
now2 = nxt[now2][d];
now3 = nxt[now3][d];
}
return max(res, x ^ a[k]);
}
void dfs(int fa, int u) {
root[u] = newnode();
insert(u, fa, a[u]);
p[u][0] = fa;
dep[u] = dep[fa] + 1;
for(int i = 0; i < (int)G[u].size(); i++) {
int v = G[u][i];
if(v != fa) {
dfs(u, v);
}
}
}
int main() {
while(~scanf("%d%d", &n, &m)) {
L = 1;
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
G[i].clear();
}
for(int i = 1; i < n; i++) {
int u, v;
scanf("%d%d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
}
dfs(0, 1);
init();
while(m--) {
int x, y, z;
scanf("%d%d%d", &x, &y, &z);
printf("%d\n", query(x, y, z));
}
}
return 0;
}

hdu4757(可持久化 Trie )的更多相关文章

  1. HDU 4757 Tree(可持久化Trie+Tarjan离线LCA)

    Tree Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 102400/102400 K (Java/Others) Total Su ...

  2. 【BZOJ4260】 Codechef REBXOR 可持久化Trie

    看到异或就去想前缀和(⊙o⊙) 这个就是正反做一遍最大异或和更新答案 最大异或就是很经典的可持久化Trie,从高到低贪心 WA: val&(1<<(base-1))得到的并不直接是 ...

  3. 可持久化Trie & 可持久化平衡树 专题练习

    [xsy1629]可持久化序列 - 可持久化平衡树 http://www.cnblogs.com/Sdchr/p/6258827.html [bzoj4260]REBXOR - Trie 事实上只是一 ...

  4. HDU 4757 Tree(可持久化trie)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4757 题意:给出一棵树,节点有权值.每次询问x到y的路径上与z抑或的最大值. 思路:可持久化trie. ...

  5. 可持久化trie 学习总结

    QAQ 以前一直觉得可持久化trie很难,今天强行写了一发觉得还是蛮简单的嘛 自己的模板是自己手写的,写了几道题目并没有出过错误 THUSC的第二题的解法五貌似就是可持久化trie,时间复杂度O(60 ...

  6. [BZOJ 4103] [Thu Summer Camp 2015] 异或运算 【可持久化Trie】

    题目链接:BZOJ - 4103 题目分析 THUSC滚粗之后一直没有写这道题,从来没写过可持久化Trie,发现其实和可持久化线段树都是一样的.嗯,有些东西就是明白得太晚. 首先Orz ZYF-ZYF ...

  7. bzoj 2741: 【FOTILE模拟赛】L 分塊+可持久化trie

    2741: [FOTILE模拟赛]L Time Limit: 15 Sec  Memory Limit: 162 MBSubmit: 1116  Solved: 292[Submit][Status] ...

  8. bzoj 2741 分块+可持久化trie

    多个询问l,r,求所有子区间异或和中最大是多少 强制在线 做法: 分块+可持久化trie 1.对于每块的左端点i,预处理出i到任意一个j,()i,j)间所有子区间异或和中最大为多少,复杂度O(\(n\ ...

  9. 【BZOJ2741】【块状链表+可持久化trie】FOTILE模拟赛L

    Description FOTILE得到了一个长为N的序列A,为了拯救地球,他希望知道某些区间内的最大的连续XOR和. 即对于一个询问,你需要求出max(Ai xor Ai+1 xor Ai+2 .. ...

  10. HDU4757--Tree 可持久化trie + LCA

    题意:n个点的树,Q次询问,询问u-v路径上的点的权值与z抑或的最大值. 先考虑,在一个区间上的问题,可以先建一个可持久化的Trie,然后每次询问,就和线段树的操作差不多,从最高位开始考虑选1还是选0 ...

随机推荐

  1. 【题解】APIO2013机器人

    其实这题前前后后的思考时间加起来应该有两天之久了,dp状态,转移方式等等都还是比较好想,然而左看右看觉得spfa复杂度未免太爆炸……然后选择看了一篇题解,发现在多重优化之下,其实是可以过的…… 首先建 ...

  2. [TJOI2018]数学计算 线段树

    ---题面--- 题解: ,,,考场上看到这题,没想到竟然是省选原题QAQ,考场上把它当数学题想了好久,因为不知道怎么处理有些数没有逆元的问题....知道这是线段树后恍然大悟. 首先可以一开始就建出一 ...

  3. 02.Java面向对象问题

    目录介绍 2.0.0.1 重载和重写的区别?重载和重写绑定机制有何区别?父类的静态方法能否被子类重写? 2.0.0.2 封装.继承.多态分别是什么? 2.0.0.3 接口和抽象类的区别是什么?接口的意 ...

  4. poj3347 Kadj Squares (计算几何)

    D - Kadj Squares Time Limit:2000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u Su ...

  5. HDU.2095(异或运算)

    find your present (2) Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Other ...

  6. Kruskal算法及其类似原理的应用——【BZOJ 3654】tree&&【BZOJ 3624】[Apio2008]免费道路

    首先让我们来介绍Krukal算法,他是一种用来求解最小生成树问题的算法,首先把边按边权排序,然后贪心得从最小开始往大里取,只要那个边的两端点暂时还没有在一个联通块里,我们就把他相连,只要这个图里存在最 ...

  7. git使用笔记(二)分支与合并

    By francis_hao    Nov 18,2016 查看分支,* 表示当前所在分支 $ git branch 查看分支和最后一次提交记录 $ git branch -v 新建分支 $ git ...

  8. 从零开始学习MXnet(二)之dataiter

    MXnet的设计结构是C++做后端运算,python.R等做前端来使用,这样既兼顾了效率,又让使用者方便了很多,完整的使用MXnet训练自己的数据集需要了解几个方面.今天我们先谈一谈Data iter ...

  9. HDU4289:Control(最小割)

    Control Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Sub ...

  10. Good Substrings CodeForces - 271D

    You've got string s, consisting of small English letters. Some of the English letters are good, the ...