题目链接

题意:有一棵树,树根为1,树上的每个结点都有一个数字x。给出Q组询问,每组询问有两个值u,x,代表询问以结点u为根的子树中的某一个数与x的最大异或值。

解法一:dfs序+可持久化字典树。看到子树询问,首先要想到dfs序啦。可以对所有结点按dfs序依次建立可持久化的字典树,字典树上的每个结点除了要保存它的后继结点以外,还要保存这个结点出现的次数num。询问结点u时,对[bg[u],ed[u]]上的字典树的num做差,字典树上剩下的num>0的结点即为可行状态,然后按普通的字典树的查询方法查询就是了。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+;
const int M=;
int n,Q,head[N],nxt[N],to[N],val[N],nEdge;
int rt[N],num[N*],go[N*][],nnode;
int bg[N],ed[N],tot;
void AddEdge(int u,int v) {
nxt[nEdge]=head[u],to[nEdge]=v,head[u]=nEdge++;
} int update(int v,int x,int bit) {
int u=++nnode;
num[u]=num[v]+;
if(bit<)return u;
int t=(x>>bit)&;
go[u][t]=update(go[v][t],x,bit-);
go[u][t^]=go[v][t^];
return u;
} void dfs(int u) {
bg[u]=++tot;
rt[tot]=update(rt[tot-],val[u],M);
for(int e=head[u]; ~e; e=nxt[e]) {
int v=to[e];
dfs(v);
}
ed[u]=tot;
} int query(int u,int v,int x,int bit,int now) {
if(bit<)return now;
int t=(x>>bit)&;
if(go[u][t^]-go[v][t^]>)
return query(go[u][t^],go[v][t^],x,bit-,now|(<<bit));
else return query(go[u][t],go[v][t],x,bit-,now);
} int main() {
while(scanf("%d%d",&n,&Q)==) {
memset(head,-,sizeof head);
nEdge=nnode=tot=;
for(int i=; i<=n; ++i)scanf("%d",&val[i]);
for(int i=; i<=n; ++i) {
int u;
scanf("%d",&u);
AddEdge(u,i);
}
rt[]=go[][]=go[][]=num[]=;
dfs();
while(Q--) {
int u,x;
scanf("%d%d",&u,&x);
printf("%d\n",query(rt[ed[u]],rt[bg[u]-],x,M,));
}
}
return ;
}

解法二:离线+字典树合并。可以自底而上来回答询问,每回答完一个结点下的所有子节点的询问,就将这个结点的字典树与它所有子节点的字典树合并。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+;
const int M=;
int n,Q,head[N],nxt[N],to[N],val[N],nEdge;
int rt[N],go[N*][],nnode,ans[N];
struct QUERY {
int x,i;
};
vector<QUERY> qr[N];
void AddEdge(int u,int v) {
nxt[nEdge]=head[u],to[nEdge]=v,head[u]=nEdge++;
} int build(int x,int bit) {
int u=++nnode;
if(bit<)return u;
int t=(x>>bit)&;
go[u][t]=build(x,bit-);
go[u][t^]=;
return u;
} int Merge(int u,int v,int bit) {
if(!u)return v;
if(!v)return u;
if(bit<)return u;
go[u][]=Merge(go[u][],go[v][],bit-);
go[u][]=Merge(go[u][],go[v][],bit-);
return u;
} int query(int u,int x,int bit,int now) {
if(bit<)return now;
int t=(x>>bit)&;
if(go[u][t^])
return query(go[u][t^],x,bit-,now|(<<bit));
else return query(go[u][t],x,bit-,now);
} void dfs(int u) {
for(int e=head[u]; ~e; e=nxt[e]) {
int v=to[e];
dfs(v);
}
for(int e=head[u]; ~e; e=nxt[e]) {
int v=to[e];
rt[u]=Merge(rt[u],rt[v],M);
}
for(int i=; i<qr[u].size(); ++i) {
ans[qr[u][i].i]=query(rt[u],qr[u][i].x,M,);
}
} int main() {
while(scanf("%d%d",&n,&Q)==) {
memset(head,-,sizeof head);
nEdge=nnode=;
for(int i=; i<=n; ++i)qr[i].clear();
for(int i=; i<=n; ++i)scanf("%d",&val[i]);
for(int i=; i<=n; ++i) {
int u;
scanf("%d",&u);
AddEdge(u,i);
}
for(int i=; i<=n; ++i)rt[i]=build(val[i],M);
for(int i=; i<Q; ++i) {
int u,x;
scanf("%d%d",&u,&x);
qr[u].push_back({x,i});
}
dfs();
for(int i=; i<Q; ++i)printf("%d\n",ans[i]);
}
return ;
}

还有一种解法是“可持久化字典树合并”,即父结点继承所有子结点的字典树,继承的方式与字典树合并一样,只不过把两棵树的公共部分开新结点就好,也是在线的,但空间消耗较大。

#define FRER() freopen("i.txt","r",stdin)
#define FREW() freopen("o.txt","w",stdout)
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+;
const int M=;
int n,Q,head[N],nxt[N],to[N],val[N],nEdge;
int rt[N],go[N*][],nnode,ans[N];
void AddEdge(int u,int v) {
nxt[nEdge]=head[u],to[nEdge]=v,head[u]=nEdge++;
}
int build(int x,int bit) {
int u=++nnode;
if(bit<)return u;
int t=(x>>bit)&;
go[u][t]=build(x,bit-);
go[u][t^]=;
return u;
} int Merge(int u,int v,int bit) {
if(!u)return v;
if(!v)return u;
if(bit<)return u;
int w=++nnode;
go[w][]=Merge(go[u][],go[v][],bit-);
go[w][]=Merge(go[u][],go[v][],bit-);
return w;
}
int query(int u,int x,int bit,int now) {
if(bit<)return now;
int t=(x>>bit)&;
if(go[u][t^])
return query(go[u][t^],x,bit-,now|(<<bit));
else return query(go[u][t],x,bit-,now);
}
void dfs(int u) {
for(int e=head[u]; ~e; e=nxt[e]) {
int v=to[e];
dfs(v);
}
for(int e=head[u]; ~e; e=nxt[e]) {
int v=to[e];
rt[u]=Merge(rt[u],rt[v],M);
}
} int main() {
while(scanf("%d%d",&n,&Q)==) {
memset(head,-,sizeof head);
nEdge=nnode=;
for(int i=; i<=n; ++i)scanf("%d",&val[i]);
for(int i=; i<=n; ++i) {
int u;
scanf("%d",&u);
AddEdge(u,i);
}
for(int i=; i<=n; ++i)rt[i]=build(val[i],M);
dfs();
for(int i=; i<Q; ++i) {
int u,x;
scanf("%d%d",&u,&x);
printf("%d\n",query(rt[u],x,M,));
}
}
return ;
}

HDU - 6191 Query on A Tree (可持久化字典树/字典树合并)的更多相关文章

  1. HDU 6191 Query on A Tree(可持久化Trie+DFS序)

    Query on A Tree Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 132768/132768 K (Java/Othe ...

  2. HDU 6191 Query on A Tree(可持久化Trie)

    题意 \(n\) 个点的有根树,根为 \(1\) .每个点有点权,有 \(q\) 个询问,每次询问以 \(u\) 为根的子树的点的点权中异或 \(x\) 所得的最大值是多少. 思路 求出整棵树的 \( ...

  3. [hdu 6191] Query on A Tree

    Query on A Tree Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 132768/132768 K (Java/Othe ...

  4. HDU 6191 Query on A Tree(字典树+离线)

    Query on A Tree Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 132768/132768 K (Java/Othe ...

  5. HDU 6191 Query on A Tree ( 2017广西邀请赛 && 可持久化Trie )

    题目链接 题意 : 给你一棵树.树上的每个点都有点权.之后有若干次问询.每次问询给出一个节点编号以及一个整数 X .问你以给出节点为根的子树中哪个节点和 X 异或最大.输出这个值 分析 : 看到这种树 ...

  6. HDU 6191 2017ACM/ICPC广西邀请赛 J Query on A Tree 可持久化01字典树+dfs序

    题意 给一颗\(n\)个节点的带点权的树,以\(1\)为根节点,\(q\)次询问,每次询问给出2个数\(u\),\(x\),求\(u\)的子树中的点上的值与\(x\)异或的值最大为多少 分析 先dfs ...

  7. HDU 4836 The Query on the Tree lca || 欧拉序列 || 动态树

    lca的做法还是非常明显的.简单粗暴, 只是不是正解.假设树是长链就会跪,直接变成O(n).. 最后跑的也挺快,出题人还是挺阳光的.. 动态树的解法也是听别人说能ac的.预计就是放在splay上剖分一 ...

  8. QTREE3 spoj 2798. Query on a tree again! 树链剖分+线段树

    Query on a tree again! 给出一棵树,树节点的颜色初始时为白色,有两种操作: 0.把节点x的颜色置反(黑变白,白变黑). 1.询问节点1到节点x的路径上第一个黑色节点的编号. 分析 ...

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

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

随机推荐

  1. JavaScript:学习笔记(3)——正则表达式的应用

    JavaScript:正则表达式的应用 应用正则表达式对象RegExp 创建正则表达式 JavaScript中使用RegExp对象来表述一个正则表达式.使用正则表达式之前首先要创建一个RegExp对象 ...

  2. rails 增删改查

    class InvoicesController < ApplicationController def index @invoices = Invoice.all end def show @ ...

  3. ERROR 2003 (HY000): Can't connect to MySQL server on "" (113)

    服务器为centos6. 这个原因是因为防火墙的问题 在mysql服务端执行 service iptables stop chkconfig iptables off #永久关闭防火墙 看情况执行 然 ...

  4. 收集整理的awk用法小结

    awk 用法:awk ‘ pattern {action} ‘ 变量名 含义 ARGC 命令行变元个数 ARGV 命令行变元数组 FILENAME 当前输入文件名 FNR 当前文件中的记录号 FS 输 ...

  5. Qt浅谈之二十六图片滑动效果

    一.简介 博客中发现有作者写的仿360的代码,觉得其中图片滑动的效果很有意思,特提取其中的代码.并加上类似mac的画面移动的动画效果. 二.详解 1.代码一:界面滑动(QWidget) (1)slid ...

  6. python统计代码行数

    以前写了一个java的统计代码行数的小程序,最近在看python,于是就参考前辈的代码,写了一个统计文件夹下面各种程序的代码的小程序,这里贴出来供大家参考 参考链接: https://gist.git ...

  7. 剑指Offer——链表中倒数第k个节点

    Question 输入一个链表,输出该链表中倒数第k个结点. Solution 一种想法就是扫描两边,第一遍求出总的节点个数,第二遍从头开始走n-k个 第二种思想类似于fast-slow指针的方法,f ...

  8. HBase-存储-HFile格式

    HBase-存储-HFile格式 实际的存储文件功能是由HFile类实现的,它被专门创建以达到一个目的:有效地存储HBase的数据.它们基于Hadoop的TFile类,并模仿Google的BigTab ...

  9. 经典的MapReduce1解析

    MapReduce1任务图解 最顶层包含4个独立的实体客户端,提交MapReduce作业jobtracker,协调作业的运行.Jobtracker是一个Java应用程序,它的主类是JobTracker ...

  10. getchar与scanf区别

    scanf可以一次按照设定的输入格式输入多个变量数据.如int d,float f,char str[20],scanf("%d%f%s",d,f,str); getchar()只 ...