题目链接:点击查看

题目大意:给出一棵树,每条边上都有一个权值,给出m个查询:a,b:问从点1到点a的唯一路径上,在边权小于等于b的边中选出边权最大的值输出,若没有符合条件的边则输出-1;

题目分析:如果还不清楚树链剖分,这里有一个讲的很棒的博客:点击查看

然后看到这里就默认已经掌握了树链剖分和线段树的基础知识了。

下面分析一下这个题目的思路,题目很好理解,但如果直接暴力模拟的话必定超时,所以我们应该转换为logn级别的复杂度再下手,关于求不超过一个值的这种题,之前我做过一个不在树上的,是问玛丽奥能跳多高的一个题,用到的方法是离线处理,然后给查询按照高度排序,最后根据根据从低到高遍历,每次加入不超过此次高度的点,用线段树维护数量即可,这个题目也是类似,因为最后要求的是满足条件的最大值,所以我们可以用线段树维护区间内的最大值,然后为了让所得到的结果都能满足条件,我们可以先将m个查询储存下来,离线处理,按照权值从小到大排序,每次插入小于等于该权值的边,然后再用线段树查询区间即可,思路是有了,我们接下来的问题就是,如何将刚才的思路在树上实现呢,因为学习了dfs序,学会了可以将树上的点转化为线性的已知条件来使用,但dfs序的前提是必须是有关于祖先和子节点的问题,而这个题目的前提是必须在树上的一条链上。

所以这篇博客一开始我就让大家先学习了一下树链剖分的知识。树链剖分,顾名思义,就是将一棵树,分成一条一条的链子,这样就形成了一种线性结构,就可以利用上面的思路在树上实现了。我在大体的讲一下如何在树上用树链剖分来实现这个题目:

首先,我们先用树链剖分,找出每条重链,然后依据重链的顺序,依次给每个点进行重新编号,让每条重链上的点都挨在一起,这样的目的就是方便使用线段树进行查询。然后再用一个辅助数组记录每条重链的端点,也就是每条重链上深度最小的那个点,如果一条重链上只有一个点,那么该点的端点就是它本身。我们现在已经完成这两个操作后就可以利用线段树进行模拟了,因为我们经过树链剖分的处理后,每个节点都获得了一个全新的序号,那么我们按照新的序号进行建树,接下来的所有操作,无论是查询还是更新,都是基于新的序号来处理的,这里务必要理解!!接着按照上述思路进行,首先要对每条边进行处理,因为权值都在边上,我们不太好操作,所以我们将权值转移到点上,转移到终点上即可,比如u->v的权值为w,那么就记为点v的权值为w,然后对m个查询先储存下来并排序处理,随后遍历一遍每个查询,然后将小于等于该查询的点加入到线段树中,意思就是将权值小于等于该查询的点加入到线段树中,更新最大值,等到权值小于等于该查询的所有点都已经加入到线段树中后,对该查询进行求结果的处理,我们该怎么求结果呢?因为如果放在树上来说,起点是点1,终点是查询中的点a,那么我们需要遍历一边点1到点a的路径(路径一定是唯一的),扫一遍路径并维护最大值即可,那么我们首先要确定点a和点1的关系,即点a和点1是否在一条重链上,因为点1是整棵树的根节点,所以点1的端点就是1,我们先看一下点a的端点是什么:

  1. 如果点a的端点是点1的话,那么点a和点1都在一条重链上,刚才我们已经将一条重链上的点都重新编号,并让他们挨在一起了,所以我们现在只需要查询点1的新编号到点a的新编号这个区间内的最大值就是答案了。
  2. 如果点a的端点不是点1,那么说明点a和点1不在一条重链上,我们可以先沿着点a走到点a的端点上,然后在走到端点的父节点上,然后在顺着走到端点的父节点的端点上,以此类推一直往上走,直到走到点1为止,这段路径一定是唯一的

因为无论很麻烦的走,还是很简单的走,点a到点1的路径都是唯一的(因为在树上两点之间的路径一定是唯一的),又因为每条重链上的点的编号都是相互挨着的,所以当我们在每条重链上经过的时候,都可以利用线段树来查询这段路上满足条件的最大值,直到走到点1为止,这样最后的答案就一定是一路上维护而来的最大值了。

对了,这个题目中好像不需要记录深度这个属性,因为点1作为起点,它的深度一定是最小的,因为是总根节点嘛,不过写都写了,就当是练习了一下树链剖分的模板吧-.-

一定要记住!!要用新的编号来建树,用新的编号来操作!!刚学树链剖分的时候就是这里不太理解,导致看别人的例程都看不懂。。上代码了,更多的细节在注释中解释:

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<stack>
#include<queue>
#include<map>
#include<cmath>
#include<set>
#include<sstream>
using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=1e5+100;
/***************************************************************************/
//以下是简单的线段树维护最大值,不多解释了,点更新+区间查询
//因为不习惯写类,还是都拆散开都把函数放到外面来舒服点
struct vvv
{int id,w;bool operator<(vvv a)const{return w<a.w;}
}val[N];struct Node
{int l,r,mmax;
}tree[N<<2];void build(int k,int l,int r)
{tree[k].l=l;tree[k].r=r;tree[k].mmax=0;if(l==r)return;int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);
}void pushup(int k)
{tree[k].mmax=max(tree[k<<1].mmax,tree[k<<1|1].mmax);
}void update(int k,int pos,int val)
{if(tree[k].l==tree[k].r){tree[k].mmax=val;return;}int mid=tree[k].l+tree[k].r>>1;if(mid>=pos)update(k<<1,pos,val);elseupdate(k<<1|1,pos,val);pushup(k);
}int query(int k,int l,int r)
{if(tree[k].l>r||tree[k].r<l)return 0;if(tree[k].l>=l&&tree[k].r<=r)return tree[k].mmax;return max(query(k<<1,l,r),query(k<<1|1,l,r));
}
//线段树到此为止
/***************************************************************************/
struct edge
{int u,v,w;bool operator<(edge a)const{return w<a.w;}
}edge[N];//结构体存边,用于在给每个节点重新编号后,给其赋权值vector<int>node[N];//vector存边int num[N],son[N],fa[N],deep[N];//num:子树大小,son:重儿子,fa:父节点,deep:深度void dfs1(int u,int f,int d)//求fa,num,deep,son
{num[u]=1;fa[u]=f;deep[u]=d;son[u]=0;for(int i=0;i<node[u].size();i++){int v=node[u][i];if(v==f)continue;dfs1(v,u,d+1);num[u]+=num[v];if(!son[u]||num[son[u]]<num[v]){son[u]=v;}}
}int top[N],id[N],cnt;//top:重链的端点,id:全新的编号void dfs2(int u,int root)//求top,id
{top[u]=root;id[u]=++cnt;if(son[u])dfs2(son[u],root);for(int i=0;i<node[u].size();i++){int v=node[u][i];if(v==son[u]||v==fa[u])continue;dfs2(v,v);}
} struct qu
{int id,w,ind,ans;//id:第几个查询,w:查询的权值,ind:查询的点,ans:查询的结果bool operator<(qu a)const{return w<a.w;}
}ans[N];//存查询信息int solve(int pos)//扫一遍对于查询的点到达点1的路径,并维护最大值
{int ans=0;while(top[pos]!=1)//如果不在同一条重链上{ans=max(ans,query(1,id[top[pos]],id[pos]));//维护目前重链上的最大值pos=fa[top[pos]];//走到父节点所在的重链上}ans=max(ans,query(1,id[top[pos]],id[pos]));//当点pos和点1处于同一条重链后会跳出while,所以最后需要再维护一下最大值if(ans==0)//别忘了如果不符合条件返回-1return -1;elsereturn ans;
}int main()
{
//  freopen("input.txt","r",stdin);int w;cin>>w;while(w--){int n;scanf("%d",&n);for(int i=1;i<=n;i++)node[i].clear();for(int i=1;i<n;i++)//存边{int u,v,w;scanf("%d%d%d",&u,&v,&w);edge[i].u=u;edge[i].v=v;edge[i].w=w;node[u].push_back(v);node[v].push_back(u);}dfs1(1,-1,1);cnt=0;dfs2(1,1);int q;scanf("%d",&q);for(int i=1;i<=q;i++)//存查询{scanf("%d%d",&ans[i].ind,&ans[i].w);ans[i].id=i;}sort(ans+1,ans+1+q);//排序离线处理for(int i=1;i<n;i++){int u=edge[i].u;int v=edge[i].v;int w=edge[i].w;if(deep[u]>deep[v])swap(u,v);val[id[v]].id=id[v];val[id[v]].w=w;}sort(val+2,val+n+1);//不包含点1的其余n-1个点int t=2;build(1,1,n);for(int i=1;i<=q;i++){int w=ans[i].w;int id=ans[i].id;while(val[t].w<=w&&t<=n)//注意这里判断边界,一个是判断小于等于查询权值,一个是判断t不能越界{update(1,val[t].id,val[t].w);t++;}ans[id].ans=solve(ans[i].ind);} for(int i=1;i<=q;i++)printf("%d\n",ans[i].ans);}return 0;
}

HDU - 3804 Query on a tree(树链剖分+线段树+离线处理)相关推荐

  1. CodeForces - 609E Minimum spanning tree for each edge(最小生成树+树链剖分+线段树/树上倍增)

    题目链接:点击查看 题目大意:给出一张 n 个点和 m 条边组成的无向图,现在询问包含每一条边的最小生成树 题目分析:考虑求解次小生成树的思路: 求出最小生成树 ans 枚举每一条非树边 ( u , ...

  2. HDU 2460 Network(双连通+树链剖分+线段树)

    HDU 2460 Network 题目链接 题意:给定一个无向图,问每次增加一条边,问个图中还剩多少桥 思路:先双连通缩点,然后形成一棵树,每次增加一条边,相当于询问这两点路径上有多少条边,这个用树链 ...

  3. 【BZOJ-2325】道馆之战 树链剖分 + 线段树

    2325: [ZJOI2011]道馆之战 Time Limit: 40 Sec  Memory Limit: 256 MB Submit: 1153  Solved: 421 [Submit][Sta ...

  4. CodeForces - 160D Edges in MST(思维+tarjan/树链剖分+线段树)

    题目链接:点击查看 题目大意:给出一张 n 个点 m 条边组成的带权无向图,现在对于每条边来说,确定一下其分类: 一定是最小生成树上的边 可能是最小生成树上的边 一定不是最小生成树的边 题目分析:两种 ...

  5. P2486 [SDOI2011]染色(树链剖分+线段树)

    题干描述 输入描述 输出格式 对于每个询问操作,输出一行答案. 输入输出样例 输入 #1 复制 6 5 2 2 1 2 1 1 1 2 1 3 2 4 2 5 2 6 Q 3 5 C 2 1 1 Q ...

  6. BZOJ4127Abs——树链剖分+线段树

    题目描述 给定一棵树,设计数据结构支持以下操作 1 u v d 表示将路径 (u,v) 加d 2 u v 表示询问路径 (u,v) 上点权绝对值的和 输入 第一行两个整数n和m,表示结点个数和操作数 ...

  7. 【BZOJ2243】[SDOI2011]染色 树链剖分+线段树

    [BZOJ2243][SDOI2011]染色 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的 ...

  8. BZOJ2325[ZJOI2011]道馆之战——树链剖分+线段树

    题目描述 口袋妖怪(又名神奇宝贝或宠物小精灵)红/蓝/绿宝石中的水系道馆需要经过三个冰地才能到达馆主的面前,冰地中 的每一个冰块都只能经过一次.当一个冰地上的所有冰块都被经过之后,到下一个冰地的楼梯才 ...

  9. BZOJ3862Little Devil I——树链剖分+线段树

    题目大意: 给一棵树,每条边可能是黑色或白色(起始都是白色),有三种操作: 1.将u到v路径上所有边颜色翻转(黑->白,白->黑) 2.将只有一个点在u到v路径上的边颜色翻转 3.查询u到 ...

  10. YbtOJ-染色计划【树链剖分,线段树,tarjan】

    正题 题目大意 给出nnn个点的一棵树,每个点有个颜色aia_iai​,你每次可以选择一个颜色全部变成另一个颜色. 求最少多少次操作可以把一种颜色变成一个完整的连通块. 1≤k≤n≤2×1051\le ...

最新文章

  1. 今天看了一下攒机配置 5000元以下 参考以下
  2. LINQ to SQL语句(7)之Exists/In/Any/All/Contains
  3. 软件测试技术 homework2
  4. 【ES11(2020)】Promise 扩展 allSettled()
  5. Ubuntu14.04中安装ROS Indigo(亲测)
  6. C#中的异步调用剖析
  7. Linux中brk()系统调用,sbrk(),mmap(),malloc(),calloc()的异同【转】
  8. anki 新的卡片类型_用 Anki 建立高效复习错题体系
  9. 用反汇编理解指向指针的指针
  10. Oracle入门第二天(下)——单行函数
  11. win32汇编--创建窗口程序(RadAsm)
  12. Tif格式图片的读取与保存
  13. matlab报错Unbalanced or unexpected parenthesis or bracket.
  14. html在字体两边加直线,CSS文字两边添加横线的几种方法
  15. 天池比赛——docker初步尝试
  16. 在线音视频加速器终于实现了
  17. 家用宽带搭建个人服务器(二)
  18. DC学习(时序分析和命令)
  19. 以太网PHY寄存器分析
  20. 你真的知道Win 10中注销、睡眠、休眠的区别么?

热门文章

  1. 类装载器ClassLoader
  2. 为什么需要动态SQL?
  3. weblogic获取应用目录路径
  4. 数据类型转换_强制转换
  5. GraphQL入门之graphql-java项目的介绍
  6. 后台服务系统之搭建ZooKeeper注册中心
  7. Fanout交换器-编写消费者
  8. Redisson分布式锁实战-1:构建分布式锁
  9. SpringBoot 对象输出
  10. java mysql embedded,java-将MySQL Connector / MXJ用于应用程序的优点/缺点/替代品有哪些...