题目描述

现在有一颗以1为根节点的由n个节点组成的树,树上每个节点上都有一个权值vi。
现在有Q 次操作,操作如下:
1  x y    查询节点x的子树中与y异或结果的最大值
2 x y z    查询路径x到y上点与z异或结果最大值

输入

第一行是两个数字n, Q;
第二行是n个数字用空格隔开,第i个数字vi表示点i上的权值 
接下来n-1行,每行两个数,x,y,表示节点x与y之间有边 
接下来Q行,每一行为一个查询,格式如上所述.
1 < n, Q ≤ 100000 ,查询1中的y ≤ 2^30 ,查询2中的z ≤ 2^30

输出

对于每一个查询,输出一行,表示满足条件的最大值。

样例输入

7 5
1 3 5 7 9 2  4
1 2
1 3
2 4
2 5
3 6
3 7
1  3 5
2 4 6 3
1  5 5
2 5 7 2
1  1 9

样例输出

7
6
12
11
14
 
  刚看到这道题有点不知所措,这异或最大值怎么求?但仔细想想就能发现取异或最大值是一种贪心的思想。首先,异或的运算原理是每一位相同为0,不同为1,那么我们肯定希望能得到1而不是0。而每一位取1对答案的贡献显然是不一样的,位数高的位取1显然要使答案更大一些。对于给出的一个数z,要使一个数和他的异或结果最大,当然是从高位开始,只要这一位能与z对应的这一位不一样那么就能使答案变大。而每一位只有0或1两种取值,恰好可以和线段树的左右子树对应。那么要使一个序列中的数与z的异或结果最大,可以建一棵32层的线段树(第一层只是代表一个根节点),从上往下的每一层就代表从高到低的每一位的取值,左子树代表0,右子树代表1,把序列的每一个数按二进制判断在每层应该往左走还是往右走,然后线段树的每个节点维护区间数的个数。这样查找时只要看与z对应位不同的那边子树中是否有数,有就往那边走,这样可以最大化每一层对答案的贡献。那么怎么把树上的一棵子树或者一条路径变成一个序列?dfs序!出栈入栈序!dfs序维护每个点的子树对应的区间,出栈入栈序维护从上到下的一条链的区间,两点间路径可以看成是这两点lca分别到这两点的链。因为要求区间中与z的异或最大值,所以要用两棵主席树分别记录这两个序列每一时刻的线段树。
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int cnt;
int num;
int tot;
int n,m;
int opt;
int res1;
int res2;
int x,y,z;
int v[100010];
int d[100010];
int s[100010];
int t[100010];
int to[200010];
int in[100010];
int out[100010];
int ioth[200010];
int head[100010];
int dfsth[100010];
int f[100010][17];
int next[200010];
int root1[200010];
int root2[200010];
int ls1[10000010];
int rs1[10000010];
int ls2[10000010];
int rs2[10000010];
int sum1[10000010];
int sum2[10000010];
void add(int x,int y)
{
tot++;
next[tot]=head[x];
head[x]=tot;
to[tot]=y;
}
void dfs(int x,int fa)
{
d[x]=d[fa]+1;
f[x][0]=fa;
in[x]=++cnt;
s[x]=++num;
ioth[cnt]=x;
dfsth[num]=x;
for(int i=1;i<=16;i++)
{
f[x][i]=f[f[x][i-1]][i-1];
}
for(int i=head[x];i;i=next[i])
{
if(to[i]!=fa)
{
dfs(to[i],x);
}
}
out[x]=++cnt;
t[x]=num;
ioth[cnt]=-x;
}
int lca(int x,int y)
{
if(d[x]<d[y])
{
swap(x,y);
}
int dep=d[x]-d[y];
for(int i=0;i<=16;i++)
{
if(((1<<i)&dep)!=0)
{
x=f[x][i];
}
}
if(x==y)
{
return x;
}
for(int i=16;i>=0;i--)
{
if(f[x][i]!=f[y][i])
{
x=f[x][i];
y=f[y][i];
}
}
return f[x][0];
}
int updata1(int pre,int k,int v)
{
int rt=++res1;
ls1[rt]=ls1[pre];
rs1[rt]=rs1[pre];
sum1[rt]=sum1[pre]+1;
if(k<0)
{
return rt;
}
if(((1<<k)&v)==0)
{
ls1[rt]=updata1(ls1[pre],k-1,v);
}
else
{
rs1[rt]=updata1(rs1[pre],k-1,v);
}
return rt;
}
int query1(int l,int r,int v,int k)
{
if(k<0)
{
return 0;
}
if(((1<<k)&v)==0)
{
if(sum1[rs1[r]]-sum1[rs1[l]]>0)
{
return query1(rs1[l],rs1[r],v,k-1)+(1<<k);
}
else
{
return query1(ls1[l],ls1[r],v,k-1);
}
}
else
{
if(sum1[ls1[r]]-sum1[ls1[l]]>0)
{
return query1(ls1[l],ls1[r],v,k-1)+(1<<k);
}
else
{
return query1(rs1[l],rs1[r],v,k-1);
}
}
}
int updata2(int pre,int k,int v,int x)
{
int rt=++res2;
ls2[rt]=ls2[pre];
rs2[rt]=rs2[pre];
sum2[rt]=sum2[pre]+x;
if(k<0)
{
return rt;
}
if(((1<<k)&v)==0)
{
ls2[rt]=updata2(ls2[pre],k-1,v,x);
}
else
{
rs2[rt]=updata2(rs2[pre],k-1,v,x);
}
return rt;
}
int query2(int x,int y,int fa,int anc,int v,int k)
{
if(k<0)
{
return 0;
}
if(((1<<k)&v)==0)
{
if(sum2[rs2[x]]+sum2[rs2[y]]-sum2[rs2[fa]]-sum2[rs2[anc]]>0)
{
return query2(rs2[x],rs2[y],rs2[fa],rs2[anc],v,k-1)+(1<<k);
}
else
{
return query2(ls2[x],ls2[y],ls2[fa],ls2[anc],v,k-1);
}
}
else
{
if(sum2[ls2[x]]+sum2[ls2[y]]-sum2[ls2[fa]]-sum2[ls2[anc]]>0)
{
return query2(ls2[x],ls2[y],ls2[fa],ls2[anc],v,k-1)+(1<<k);
}
else
{
return query2(rs2[x],rs2[y],rs2[fa],rs2[anc],v,k-1);
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&v[i]);
}
for(int i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
dfs(1,0);
for(int i=1;i<=num;i++)
{
root1[i]=updata1(root1[i-1],30,v[dfsth[i]]);
}
for(int i=1;i<=cnt;i++)
{
root2[i]=updata2(root2[i-1],30,v[abs(ioth[i])],ioth[i]>0?1:-1);
}
for(int i=1;i<=m;i++)
{
scanf("%d",&opt);
if(opt==1)
{
scanf("%d%d",&x,&y);
printf("%d\n",query1(root1[s[x]-1],root1[t[x]],y,30));
}
else
{
scanf("%d%d%d",&x,&y,&z);
int anc=lca(x,y);
printf("%d\n",query2(root2[in[x]],root2[in[y]],root2[in[anc]],root2[in[f[anc][0]]],z,30));
}
}
}

BZOJ5338[TJOI2018]xor——主席树+dfs序的更多相关文章

  1. 51 nod 1681 公共祖先 (主席树+dfs序)

    1681 公共祖先 基准时间限制:1 秒 空间限制:131072 KB 分值: 80 难度:5级算法题   有一个庞大的家族,共n人.已知这n个人的祖辈关系正好形成树形结构(即父亲向儿子连边). 在另 ...

  2. 【BZOJ1803】Spoj1487 Query on a tree III 主席树+DFS序

    [BZOJ1803]Spoj1487 Query on a tree III Description You are given a node-labeled rooted tree with n n ...

  3. 【BZOJ-3545&3551】Peaks&加强版 Kruskal重构树 + 主席树 + DFS序 + 倍增

    3545: [ONTAK2010]Peaks Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1202  Solved: 321[Submit][Sta ...

  4. 【SPOJ】10628. Count on a tree(lca+主席树+dfs序)

    http://www.spoj.com/problems/COT/ (速度很快,排到了rank6) 这题让我明白了人生T_T 我知道我为什么那么sb了. 调试一早上都在想人生. 唉. 太弱. 太弱. ...

  5. BZOJ 2809: [Apio2012]dispatching [主席树 DFS序]

    传送门 题意:查询树上根节点值*子树中权值和$\le m$的最大数量 最大值是多少 求$DFS$序,然后变成区间中和$\le m$最多有几个元素,建主席树,然后权值线段树上二分就行了 $WA$:又把边 ...

  6. BZOJ - 2809 dispatching 主席树+dfs序

    在一个忍者的帮派里,一些忍者们被选中派遣给顾客,然后依据自己的工作获取报偿.在这个帮派里,有一名忍者被称之为 Master.除了 Master以外,每名忍者都有且仅有一个上级.为保密,同时增强忍者们的 ...

  7. BZOJ3772 精神污染 主席树 dfs序

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ3772 题意概括 给出一个树,共n个节点. 有m条互不相同的树上路径. 现在让你随机选择2条路径,问 ...

  8. BZOJ3545&3551[ONTAK2010]Peaks——kruskal重构树+主席树+dfs序+树上倍增

    题目描述 在Bytemountains有N座山峰,每座山峰有他的高度h_i.有些山峰之间有双向道路相连,共M条路径,每条路径有一个困难值,这个值越大表示越难走,现在有Q组询问,每组询问询问从点v开始只 ...

  9. Codeforces 893F(主席树+dfs序)

    在子树内和距离不超过k是一个二维限制,容易想到主席树,但主席树显然没法查最小值,因为不满足区间可减.kdtree和二维线段树可以干这事,但肯定会T飞.但事实上我们的问题有一个特殊性:对某个点x,查询其 ...

随机推荐

  1. 1kb的前端HTML模板解析引擎,不限于嵌套、循环、函数你能想到的解析方式

    传送门:https://github.com/xiangyuecn/BuildHTML copy之前说点什么 html做点小功能(什么都没有),如果是要手动生成html这种操作,容易把代码搞得乱七八糟 ...

  2. Spring学习日志之纯Java配置的MVC框架搭建

    依赖引入 <dependencies> <dependency> <groupId>javax.servlet</groupId> <artifa ...

  3. Error【0006】:could not create or update nagios.configtest

    1. 错误背景 在本系列博客<Nagios监控系统部署(源码).md>中(笔记内链:Nagios监控系统部署(源码).md,博客园地址:https://www.cnblogs.com/li ...

  4. linux RPM manager

    RPM manage:rpm2cpio package_name | cpio -id #将一个rpm包解压至当前目录rpm -qi package_name #查看一个已安装的rpm包信息rpm - ...

  5. Docker容器学习梳理 - 容器时间跟宿主机时间同步

    在Docker容器创建好之后,可能会发现容器时间跟宿主机时间不一致,这就需要同步它们的时间,让容器时间跟宿主机时间保持一致.如下: 宿主机时间 [root@slave-1 ~]# date Fri M ...

  6. php 中 opendir() readdir() scandir()

    opendir(path,context)若成功,则该函数返回一个目录流,否则返回 false 以及一个 error.可以通过在函数名前加上 “@” 来隐藏 error 的输出. readdir() ...

  7. Python_每日习题_0001_数字组合

    # Topic: There are four digits: 1, 2, 3 and 4. # How many different three digits can be formed witho ...

  8. 《Linux内核分析》第六周学习笔记

    <Linux内核分析>第六周学习笔记 进程的描述和创建 郭垚 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/co ...

  9. Linux内核第六节 20135332武西垚

    如何描述一个进程:进程描述符的数据结构: 如何创建一个进程:内核是如何执行的,以及新创建的进程从哪里开始执行: 使用gdb跟踪新进程的创建过程. 进程的描述 操作系统三大功能: 进程管理(最核心最基础 ...

  10. Freemaker的了解

    freemarket 模板技术 与web容器没什么关系 可以用struct2作为视图组件   第一步导入jar包 项目目录下建立一个templates目录 在此目录下建立一个模板文件a.ftl文件   ...