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. Hibernate查询语言——HQL

    HQL(Hibernate Query Language)查询语言是完全面向对象的查询语言,它提供了更加面向对象的封装,可以理解如多态.继承和关联. HQL的基本语法如下: select " ...

  2. [洛谷P1231] 教辅的组成

    题目大意:有n1本书,n2本练习册和n3个答案,然后又一些条件,说明某本答案可能和某本书对应,某本练习册可能和某本书对应,求最多有多少本完整的书(有书,练习册,答案) 题解:网络流,对应就连边,然后考 ...

  3. SetLocalTime设置本地时间

    /***************************************************************** 函数名:EnableSetTimePriviledge 功 能:开 ...

  4. MySQL之SELECT 语句详解

    本文参考实验楼的SELECT 语句详解结合自己操作部分而写成. 注意:大多数系统中,SQL语句都是不区分大小写的,但是出于严谨和便于区分保留字和变量名,在书写的时,保留字应大写,而变量名应小写.所谓的 ...

  5. 【BZOJ 3123】 [Sdoi2013]森林 主席树启发式合并

    我们直接按父子关系建主席树,然后记录倍增方便以后求LCA,同时用并查集维护根节点,而且还要记录根节点对应的size,用来对其启发式合并,然后每当我们合并的时候我们都要暴力拆小的一部分重复以上部分,总时 ...

  6. Codeforces Round #532 (Div. 2) 题解

    Codeforces Round #532 (Div. 2) 题目总链接:https://codeforces.com/contest/1100 A. Roman and Browser 题意: 给出 ...

  7. HDU1013 Digital Roots

    http://acm.hdu.edu.cn/showproblem.php?pid=1013 #include<iostream> #include "cstdio" ...

  8. codefoeces problem 671D——贪心+启发式合并+平衡树

    D. Roads in Yusland Mayor of Yusland just won the lottery and decided to spent money on something go ...

  9. codevs1063 合并果子 优先队列(小根堆)

    题目传送门 这道题很容易想到优先把两堆重量最小的合并比较优 然后乱搞一下就可以啦 #include<cstdio> #include<cstring> #include< ...

  10. [BZOJ3033]太鼓达人|欧拉图

    Description 七夕祭上,Vani牵着cl的手,在明亮的灯光和欢乐的气氛中愉快地穿行.这时,在前面忽然出现了一台太鼓达人机台,而在机台前坐着的是刚刚被精英队伍成员XLk.Poet_shy和ly ...