传送门

Tree

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 102400/102400 K (Java/Others)

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$ (including 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\le n \le 10^{5}$) and $m$ ($1 \le m \le 10^{5}$), which are the amount of tree’s nodes and queries, respectively.

The second line contains $n$ integers $a_1, a_2, \dots, a_n$ and $a_i$ ($0 \le a_i <2^{16}$) is the value on the $i$th node.

The next $n–1$ lines contains two integers $u, v$, which means there is an edge 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
 
Source

Solution
可持久化 trie 。
trie可以解决关于异或的一个经典问题:
给定非负整数序列 $a_1,\cdots, a_n$ 和非负整数 $x$ 求
\[\max_{1 \le i \le n} a_i \text{^} x\]
做法:
将 $a_1$ 到 $a_n$ 的二进制形式(二进制串, 高位在前)插入到 trie 里, 用 $x$ 的二进制串在建好的 trie 里贪心匹配.
这种做法可推广到这个题目上:
我们对每个节点 $u$ 建立一棵 trie ,里面存的是从根节点到 $u$ 的路径上的所有节点的权值的二进制串.
先不考虑如何建这 $n$ 棵 trie ,我们考虑如何利用这 $n$ 棵 tire 完成题目要求的查询.
我们用 $\text{lca}(u,v)$ 表示 $u,v$ 的最近公共祖先(LCA), $\text{trie}_u$ 表示节点 $u$ 对应的 trie 。
对于询问 $(u, v, x)$ ,以 $x$ 的二进制串在 $\text{trie}_u, \text{trie}_v$ 和 $\text{trie}_{\text{lca}(u,v)}$ 中同步贪心匹配.
注意: 在贪心匹配的每个阶段, 我们匹配到的都是某个数的二进制串的某个前缀, 这个前缀属于哪个数是不知道的, 那么如何保证最后匹配到的那个二进制串对应的数出现在 $u$ 到 $v$ 的路径上呢?
 
可以这样做:
首先应当明确: tire 的每个节点都代表一个二进制串(二进制前缀). 我们对 tire 的每个节点维护一个值 $cnt$ 表示在这棵 tire 里有多少个(完整的)二进制串包含该节点对应的前缀(即该二进制前缀出现的次数).
 
这样欲匹配某个前缀时, 我们就能判断该前缀是否(也)属于$u$到$v$的路径上某个节点的权值对应的二进制串. 只有当该前缀合法时才能继续匹配.
 
具体而言, 设目前要匹配的二进制前缀在 $\text{trie}_u, \text{trie}_v$ 和 $\text{trie}_{\text{lca}(u,v)}$ 分别对应于节点 $p,q,r$, 则要求:\[p.cnt+q.cnt>2\times r.cnt\]
 
这样做有一个潜在的 bug:
当 $a_{\text{lca}(u,v)}$ 是唯一最优解时, 会被忽略.  所以最后还要用 $x\text{^}a_{\text{lca}(u,v)}$ 更新答案.
 
当然这个麻烦也是可应避免的, 只要把 $\text{trie}_{\text{lca}(u,v)}$ 换成 $\text{trie}_{\text{par}(\text{lca}(u,v))}$ —— $\text{par}(u)$ 表示 $u$ 的父节点——就可以了.
 
现在考虑建 $n$ 棵 trie 的问题. 我们没必要分别建 $n$ 棵 trie, 而且这样做空间和时间都无法承受. 考虑到 $\text{trie}_u$ 与 $\text{trie}_{\text{par}(u)}$ 的差别只是多插入了 $a_u$ 对应的二进制串, 我们可以采用可持久化 trie 来建这 $n$ 棵树. (如果读者还不了解可久化数据结构, 可参考陈立杰的《可持久化数据结构研究》的第 4 节——可持久化线段树.)

Implementation

可持久化 trie 在写法上有一个技巧: 我们一开始用一个节点表示一棵空树, 将此节点的 $cnt$ 置为 $0$. 在建树的过程中如果某个节点的某个儿子为空, 对应的指针就指向该空节点.
这样写起来就方便很多, 而且不会有 bug.
#include <bits/stdc++.h>
using namespace std; const int M{<<}, N{<<}; int a[N], nxt[M][], cnt[M], tail, root[N];
vector<int> g[N]; //a NULL NODE
//The empty tree is a well-defined tree! int Insert(int id, int v, int dep){ //this node is going to be modified
//always copy before modification
int cur=tail++;
cnt[cur]=cnt[id]+;
if(dep>=)
if(v&<<dep){
nxt[cur][]=nxt[id][];
nxt[cur][]=Insert(nxt[id][], v, dep-);
}
else{
nxt[cur][]=Insert(nxt[id][], v, dep-);
nxt[cur][]=nxt[id][];
}
else nxt[cur][]=nxt[cur][]=; //error-prone return cur;
} int fa[N][], dep[N]; void dfs(int u, int f){
fa[u][]=f, dep[u]=dep[f]+;
for(int i=; i<; i++)
fa[u][i]=fa[fa[u][i-]][i-]; root[u]=Insert(root[f], a[u], );
for(auto x:g[u])
if(x!=f)
dfs(x, u);
} int get_lca(int u, int v){
if(dep[u]<dep[v]) swap(u, v);
int diff=dep[u]-dep[v];
for(int i=; i<; i++)
if(diff&<<i) u=fa[u][i];
if(u==v) return u;
for(int i=; i>=; i--) //error-prone
if(fa[u][i]!=fa[v][i])
u=fa[u][i], v=fa[v][i];
return fa[u][];
} //keep it simple
int query(int u, int v, int x){
int res=, lca=get_lca(u, v);
int _u=root[u], _v=root[v], _w=root[lca]; for(int i=; i>=; i--){
bool f=!(x&<<i);
//check if this bit valid
if(cnt[nxt[_u][f]]+cnt[nxt[_v][f]]>cnt[nxt[_w][f]]<<) res|=<<i;
else f=!f;
_u=nxt[_u][f], _v=nxt[_v][f], _w=nxt[_w][f];
}
return max(res, x^a[lca]);
} int main(){
for(int n, m; cin>>n>>m; ){
for(int i=; i<=n; i++){
scanf("%d", a+i);
g[i].clear();
}
for(int u, v, i=; i<n; i++){
scanf("%d%d", &u, &v);
g[u].push_back(v);
g[v].push_back(u);
}
//construct an empty tree
root[]=tail=, memset(nxt[tail], , sizeof(nxt[tail])), cnt[tail]=, tail++; dfs(, );
for(int u, v, x, lca; m--; ){
scanf("%d%d%d", &u, &v, &x);
printf("%d\n", query(u, v, x));
}
}
}
 
 
 
 
 
 
 
 

HDU 4757 Tree的更多相关文章

  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. HDU 4757 Tree 可持久化字典树

    Tree Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=4757 Des ...

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

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

  4. HDU 4757 Tree 可持久化字典树 trie

    http://acm.hdu.edu.cn/showproblem.php?pid=4757 给出一棵树,每个节点有权值,每次查询节点 (u,v) 以及 val,问 u 到 v 路径上的某个节点与 v ...

  5. HDU 4757 Tree(可持续化字典树,lca)

    题意:询问树上结点x到结点y路上上的权值异或z的最大值. 任意结点权值 ≤ 2^16,可以想到用字典树. 但是因为是询问某条路径上的字典树,将字典树可持续化,字典树上的结点保存在这条路径上的二进制数. ...

  6. HDU 4757 Tree(可持久化字典树)(2013 ACM/ICPC Asia Regional Nanjing Online)

    Problem Description   Zero and One are good friends who always have fun with each other. This time, ...

  7. HDU.4757.Tree(可持久化Trie)

    题目链接 \(Description\) 给定一棵树,点有点权.\(Q\)次询问\(x,y,z\),求\(x\)到\(y\)的简单路径中,与\(z\)异或能得到的最大的数是多少. \(Solution ...

  8. hdu 5909 Tree Cutting [树形DP fwt]

    hdu 5909 Tree Cutting 题意:一颗无根树,每个点有权值,连通子树的权值为异或和,求异或和为[0,m)的方案数 \(f[i][j]\)表示子树i中经过i的连通子树异或和为j的方案数 ...

  9. HDU 5044 Tree(树链剖分)

    HDU 5044 Tree field=problem&key=2014+ACM%2FICPC+Asia+Regional+Shanghai+Online&source=1&s ...

随机推荐

  1. Webwork 学习之路【06】Action 调用

    一路走来,终于要开始 webwork 核心业务类的总结,webwork 通过对客户端传递的 web 参数重新包装,进行执行业务 Action 类,并反馈执行结果,本篇源码分析对应下图 WebWork ...

  2. 利用PhotoShop将Font-Awesome转为图片格式

    介绍如何将Font-Awesome等字体图标转换为图片格式,使用PHOTPSHOP很简单. 网上找了很多,都比较麻烦.别问为什么要这么做,因为你还没遇到需要的时候. 下载Font-Awesome字体库 ...

  3. 一个用react+nodejs实现的笔记本小应用

    寒假回家产品经理一直叮嘱着要继续做学校团队的辣个项目,但是...,我到现在一点都还没做,而且还销声匿迹躲了起来藏了几天,是的我干了票大的,想把项目用一种新的架构实现了,所以这几天一直在偷偷摸摸的做一些 ...

  4. 深入理解计算机系统(1.2)---hello world的程序是如何运行的

    在写本章的内容之前,LZ先做个小广告.其实也不算是什么广告,就是LZ为了和各位猿友交流方便,另外也确实有个别猿友留言或者在博客里发短消息给LZ要联系方式.因此LZ斗胆建立了一个有关<深入理解计算 ...

  5. js前端分页

    转载:http://www.cnblogs.com/lyzg/p/5791011.html http://www.cnblogs.com/m-m-g-y0416/p/5601903.html

  6. js队列

    用指针和数组模拟基本队列 http://blog.csdn.net/zhuwq585/article/details/53177192 js下的事件队列,或者异步队列 http://www.jb51. ...

  7. [BZOJ2429][HAOI2006]聪明的猴子(MST)

    题目:http://www.lydsy.com:808/JudgeOnline/problem.php?id=2429 分析:要让最大的最小,所以就是最小生成树上的啦,于是问题就变成了有多少个猴子&g ...

  8. tr命令

    tr命令是linux下一个字符处理命令,用途:    字符替换    字符删除    字符压缩形式:tr [OPTION]... SET1 [SET2]接口:输入输出都是标准流,所以要通过管道来调用这 ...

  9. 【CSS3】 线性渐变

    参考地址:http://www.w3cplus.com/css3/new-css3-linear-gradient.html background-image: linear-gradient(to ...

  10. 53-whereis 查找文件

    查找文件 whereis [options] file 参数 file 是whereis需要查找的文件,这些文件属于原始代码,二进制文件或是帮助文件 选项 -b               只查找二进 ...