HDU
来源:互联网 发布:紫胤真人知乎 编辑:程序博客网 时间:2024/06/09 00:23
点我看题
题意:给出一颗树,每棵树上有n个结点,每个结点对应一个值,有m组查询操作,查询在从x到y的这条路径上与z进行异或的最大值。
分析:可持久化字典树的模板题
这个题以01字典树为基础,如果不是很了解01字典树的话,可以看看ACdream1063,这题题解。
话说回来,如果我们要查询数x与某个数异或的最大值,我们应该尽量选择高位与x的高位相反的一些数。
举个栗子,我们要求5与某个数异或的最大值,假设可选的数不超过15,5的二进制为0101,因为可选的数不超过15,所以我们只要考虑后四位,
首先看最高位(第四位)存不存在等于1的数,存在的话,一直往低位走,最终可以得到结果。
这个题就要对每个结点都建一棵Trie树(感觉根主席树有点像噢,主席树是对每个结点建线段树
建Trie树的时候,我们用一个数组sz[]记录字典树对应结点前缀的数量,如果v是u的子节点,且v的权值是010,u的权值为011,假设u已经加到树中,那么在加v的时候,发现前缀01的数量已经是1了,那我们只要在这个基础上加1即可,也就是继承了父节点对它的影响,但是对于010来说是一个新的数,我们要新建一个结点然后更新他的sz,其余和父节点一致就好。
当我们去查询每一位都取反的x时,只关心每一位是否对应存在。也就是想知道u到v这条路上有没有满足条件的存在。
设pre=lca(u,v),我们就只要判断f(u)+f(v)-2×f(pre)是否大于0就好,大于0的话,就加上1>>i。
参考代码:
/*可持久化字典树*/#include<cstdio>#include<cmath>#include<cstring>#include<algorithm>#include<vector>#include<iostream>using namespace std;#define mem(a,b) memset(a,b,sizeof(a))const int maxn = 1e5+10;//原树结点const int maxv = 16;//每颗字典树的深度const int maxnode = 2e6;//字典树的结点个数,16*1e5=1600000int n,q;int a[maxn];vector<int> g[maxn];//字典树int tot;//记录字典树的总结点数int root[maxn];//记录字典树的根节点int f[maxv+2][maxn];//嘻嘻嘻嘻f[i][v]指的是结点v向上跳2^i次后得到的结点值int sz[maxnode];//记录前缀和int dep[maxn];//记录结点深度int ch[maxnode][2];//字典树儿子结点void Init(){ tot = 0; mem(root,0); mem(f,0); mem(sz,0);//前缀和初始化为0 dep[0] = 0;}int NewNode(){ mem(ch[++tot],0); return tot;}void Insert( int u, int fa, int val){ int rtu = root[u];//root[u]是字典树中真正的位置,而u是在原树中的编号 int rtf = root[fa];//同理 for( int i = 15; i >= 0; i--) { int id = (val>>i)&1;//计算val的第i位(有第0位)是1还是0 if( !ch[rtu][id])//如果第i位不存在的话,那就要新建一个结点 { ch[rtu][id] = NewNode(); ch[rtu][!id] = ch[rtf][!id];//!id就继承父节点的 sz[ch[rtu][id]] = sz[ch[rtf][id]];//前缀也继承父节点的 } rtu = ch[rtu][id];//往下走 rtf = ch[rtf][id]; sz[rtu]++; }}//遍历原树中的n个结点,对每个结点建立01字典树void dfs( int u, int fa){ f[0][u] = fa;//记录下u的父节点 dep[u] = dep[fa]+1;//当前结点的深度为其父亲的深度+1 root[u] = NewNode();//给字典树新建一个结点 Insert(u,fa,a[u]);//根据结点u对应的值建01字典树 for( int i = 0; i < g[u].size(); i++)//遍历当前结点u的叶子结点 { int v = g[u][i]; if( v != fa)//如果不是其父节点,继续深搜 dfs(v,u); }}//倍增lca//求u和v的最近公共祖先int lca( int u, int v){ if( dep[u] > dep[v])//保证u的深度小于v,也就是u和v的祖先结点在同一深度 swap(u,v); for( int i = 0; i < maxv; i++) if( ((dep[v]-dep[u])>>i) & 1)//如果条件为真,就往上跳,最终结果会与u在同一层 v = f[i][v]; if( u == v)//显然 return u; for( int i = maxv-1; i >= 0; i--) if( f[i][u] != f[i][v])//还不相等的话,就一起往上走 u = f[i][u], v = f[i][v]; return f[0][u];//最后u==v,返回自己}int Query( int u, int v, int val){ int f = lca(u,v);//求u,v的最近公共祖先 int res = a[f]^val; int rtu = root[u], rtv = root[v], rtf = root[f];//转化到字典树中去 int ret = 0; for( int i = maxv-1; i >= 0; i--) { int id = (val>>i)&1; if( sz[ch[rtu][!id]]+sz[ch[rtv][!id]]-2*sz[ch[rtf][!id]] > 0)//满足这个条件 { ret += 1<<i; id = !id; } rtu = ch[rtu][id]; rtv = ch[rtv][id]; rtf = ch[rtf][id]; } return max(ret,res);}int main(){ while( ~scanf("%d%d",&n,&q)) { Init(); for( int i = 1; i <= n; i++) { scanf("%d",&a[i]);//输入每个结点的值 g[i].clear();//清空动态数组 } int u,v; for( int i = 1; i < n; i++) { scanf("%d%d",&u,&v);//输入一条边 g[u].push_back(v); g[v].push_back(u); } dfs(1,0); for( int i = 0; i < maxv-1; i++) for( int j = 1; j <= n; j++) if( f[i][j] != 0) f[i+1][j] = f[i][f[i][j]];//j向上跳2^(i+1)次得到的结点相当于j先向上跳2^i再向上跳2^i int x,y,z; while( q--) { scanf("%d%d%d",&x,&y,&z); printf("%d\n",Query(x,y,z)); } } return 0;}
阅读全文
0 0
- hdu
- hdu
- HDU
- hdu ()
- hdu
- hdu
- HDU
- HDU
- hdu
- hdu
- HDU
- Hdu
- hdu
- hdu-
- hdu
- hdu
- hdu
- HDU
- Java十四种开发工具及其特点
- WPF循环显示图片解决OutofMemoryException方法
- SGISTL源码探究-第一级配置器
- XHTML 相对路径与绝对路径
- hive基础操作
- HDU
- bzoj 3390: [Usaco2004 Dec]Bad Cowtractors牛的报复
- [JavaScript笔记] 数组去重 与 数组随机排序
- ftp服务端本地测试用ftp://localhost或者127.0.0.1都可以测试成功,但是一旦用本机IP就不行
- fiddler 参考网站
- UVA 196
- unity图像处理(下)
- 22:津津的储蓄计划
- CAD转PDF批量转好转么