题意:给出一棵树,这棵树每个点有权值,然后有3种操作。操作一:修改树根为rt,操作二:修改u到v路径上点权值为w,操作三:询问以rt为根x子树的最小权值。

解法:如果没有修改树根操作那么这题就是树链剖分的裸题。但是修改树根操作会使得题目变得复杂一些,这里直接说结论:我们先直接以1为根建树进行树链剖分,这样的话根固定了那么路径修改操作就照常,然后我们要考虑换根操作对查询的影响(这是重点)。

画图分析后可以发现,可以分为3种情况,①x==rt,询问的就是整棵树  ②x不在1到rt的路径上,对查询没有影响,查询照常  3 x在1到rt的路径上,这样会麻烦一些,仔细观察图发现其实查询就变成了查询除去(x的rt所在的子树)那么我们就可以先用倍增找到(x的rt所在的子树)这颗子树的根为  y=rt的dep[rt]-dep[x]-1级祖先。这样查询除去y的子树剩下的左右两个区间合并就是答案了。

这道题还是很不错的,做了能涨处理这样的换根问题的姿势。

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. typedef long long LL;
  4. const LL INF=1LL<<;
  5. const int N=1e5+;
  6. int n,m,T,rt,num=,f[N][];
  7. LL v[N];
  8. struct node{
  9. int dep,fa,sz,heavy;
  10. int toseg,top; LL val;
  11. }tree[N]; //原本的树
  12. int totree[N<<]; //线段树点i代表原树的点totree[i]
  13.  
  14. /*-------------------------以下为线段树-----------------------------*/
  15. LL Min[N<<],tag[N<<];
  16. void pushdown(int rt) {
  17. if (!tag[rt]) return;
  18. Min[rt<<]=tag[rt<<]=tag[rt];
  19. Min[rt<<|]=tag[rt<<|]=tag[rt];
  20. tag[rt]=;
  21. }
  22. void pushup(int rt) {
  23. Min[rt]=min(Min[rt<<],Min[rt<<|]);
  24. }
  25.  
  26. void build(int rt,int l,int r) {
  27. if (l==r) {
  28. Min[rt]=tree[totree[l]].val; tag[rt]=;
  29. return;
  30. }
  31. int mid=l+r>>;
  32. build(rt<<,l,mid);
  33. build(rt<<|,mid+,r);
  34. pushup(rt);
  35. }
  36.  
  37. void update(int rt,int l,int r,int ql,int qr,LL v) {
  38. if (ql<=l && r<=qr) {
  39. Min[rt]=tag[rt]=v;
  40. return;
  41. }
  42. int mid=l+r>>;
  43. pushdown(rt);
  44. if (ql<=mid) update(rt<<,l,mid,ql,qr,v);
  45. if (qr>mid) update(rt<<|,mid+,r,ql,qr,v);
  46. pushup(rt);
  47. }
  48.  
  49. LL query(int rt,int l,int r,int ql,int qr) {
  50. if (ql<=l && r<=qr) return Min[rt];
  51. int mid=l+r>>; LL ret=INF;
  52. pushdown(rt);
  53. if (ql<=mid) ret=min(ret,query(rt<<,l,mid,ql,qr));
  54. if (qr>mid) ret=min(ret,query(rt<<|,mid+,r,ql,qr));
  55. return ret;
  56. }
  57.  
  58. /*--------------------------以下为树链剖分----------------------------*/
  59. int cnt=,head[N<<],to[N<<],nxt[N<<];
  60. void add_edge(int x,int y) {
  61. nxt[++cnt]=head[x]; to[cnt]=y; head[x]=cnt;
  62. }
  63.  
  64. void dfs1(int x,int fa,int dep) { //点x的父亲为fa深度为dep
  65. tree[x].dep=dep;
  66. tree[x].fa=fa;
  67. tree[x].sz=;
  68. tree[x].val=v[x];
  69. int maxson=-;
  70. for (int i=head[x];i;i=nxt[i]) {
  71. int y=to[i];
  72. if (y==fa) continue;
  73. f[y][]=x; //在dfs1的时候顺便求倍增数组
  74. for (int j=;j<=T;j++)
  75. f[y][j]=f[f[y][j-]][j-];
  76. dfs1(y,x,dep+);
  77. tree[x].sz+=tree[y].sz;
  78. if (tree[y].sz>maxson) tree[x].heavy=y,maxson=tree[y].sz;
  79. }
  80. }
  81.  
  82. void dfs2(int x,int top) { //点x所在树链的top
  83. tree[x].toseg=++num;
  84. tree[x].top=top;
  85. totree[num]=x;
  86. if (!tree[x].heavy) return; //叶子结点
  87. dfs2(tree[x].heavy,top); //先剖分重儿子
  88. for (int i=head[x];i;i=nxt[i]) { //再剖分轻儿子
  89. int y=to[i];
  90. if (y==tree[x].fa || y==tree[x].heavy) continue;
  91. dfs2(y,y);
  92. }
  93. }
  94.  
  95. void update2(int x,int y,LL z) { //修改x到y路径的值
  96. while (tree[x].top!=tree[y].top) { //不在同一条链上
  97. if (tree[tree[x].top].dep<tree[tree[y].top].dep) swap(x,y); //x为深度大的链
  98. update(,,n,tree[tree[x].top].toseg,tree[x].toseg,z); //x向上跳的同时更新
  99. x=tree[tree[x].top].fa; //深度大的向上跳
  100. }
  101. if (tree[x].dep>tree[y].dep) swap(x,y); //这里x和y在同一条链
  102. update(,,n,tree[x].toseg,tree[y].toseg,z); //x和y这条链的更新
  103. }
  104.  
  105. int getfa(int x,int k) { //取得x的k级祖先
  106. if (k<) return -;
  107. for (int i=T;i>=;i--)
  108. if (k>=(<<i)) x=f[x][i],k-=(<<i);
  109. return x;
  110. }
  111.  
  112. int main()
  113. {
  114. cin>>n>>m;
  115. T=(int)log2(n)+;
  116. for (int i=;i<n;i++) {
  117. int x,y; scanf("%d%d",&x,&y);
  118. add_edge(x,y); add_edge(y,x);
  119. }
  120. for (int i=;i<=n;i++) scanf("%lld",&v[i]);
  121. dfs1(,,);
  122. dfs2(,);
  123. build(,,n);
  124. scanf("%d",&rt);
  125. for (int i=;i<=m;i++) {
  126. LL opt,x,y,z; scanf("%lld",&opt);
  127. if (opt==) {
  128. scanf("%d",&rt);
  129. }
  130. if (opt==) {
  131. scanf("%lld%lld%lld",&x,&y,&z);
  132. update2(x,y,z);
  133. }
  134. if (opt==) {
  135. scanf("%lld",&x);
  136. if (x==rt) printf("%lld\n",query(,,n,,n)); //x就是root
  137. else if (x==getfa(rt,tree[rt].dep-tree[x].dep)) { //x在1到root的路径上
  138. y=getfa(rt,tree[rt].dep-tree[x].dep-); //取得去掉部分的根(这要画图理解)
  139. z=INF;
  140. if (tree[y].toseg->=) z=min(z,query(,,n,,tree[y].toseg-));
  141. if (tree[y].toseg+tree[y].sz<=n) z=min(z,query(,,n,tree[y].toseg+tree[y].sz,n));
  142. printf("%lld\n",z);
  143. } else { //其他情况,直接查询子树
  144. printf("%lld\n",query(,,n,tree[x].toseg,tree[x].toseg+tree[x].sz-));
  145. }
  146. }
  147. }
  148. return ;
  149. }

洛谷P3979 遥远的国度 树链剖分+分类讨论的更多相关文章

  1. 洛谷 P4114 Qtree1 树链剖分

    目录 题面 题目链接 题目描述 输入输出格式 输入格式: 输出格式: 输入输出样例 输入样例: 输出样例: 说明 说明 思路 Change Query AC代码 总结 题面 题目链接 P4114 Qt ...

  2. [洛谷P3384] [模板] 树链剖分

    题目传送门 显然是一道模板题. 然而索引出现了错误,狂wa不止. 感谢神犇Dr_J指正.%%%orz. 建线段树的时候,第44行. 把sum[p]=bv[pos[l]]%mod;打成了sum[p]=b ...

  3. 洛谷.4114.Qtree1(树链剖分)

    题目链接 模板题都错了这么多次.. //边权赋到点上 树剖模板 //注意LCA.链的顶端不能统计到答案! #include <cstdio> #include <cctype> ...

  4. 洛谷3384&bzoj1036树链剖分

    值得注意的是: 一个点的子树是存在一起的...也就是说我们修改子树的时候只用... /********************************************************* ...

  5. BZOJ 3083 遥远的国度 树链剖分

    3083: 遥远的国度 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 797  Solved: 181[Submit][Status] Descrip ...

  6. BZOJ 3083 遥远的国度(树链剖分+LCA)

    Description 描述zcwwzdjn在追杀十分sb的zhx,而zhx逃入了一个遥远的国度.当zcwwzdjn准备进入遥远的国度继续追杀时,守护神RapiD阻拦了zcwwzdjn的去路,他需要z ...

  7. 【bzoj3083】遥远的国度 树链剖分+线段树

    题目描述 描述zcwwzdjn在追杀十分sb的zhx,而zhx逃入了一个遥远的国度.当zcwwzdjn准备进入遥远的国度继续追杀时,守护神RapiD阻拦了zcwwzdjn的去路,他需要zcwwzdjn ...

  8. BZOJ 3083: 遥远的国度(树链剖分+DFS序)

    可以很显而易见的看出,修改就是树链剖分,而询问就是在dfs出的线段树里查询最小值,但由于这道题会修改根节点,所以在查询的时候需判断x是否为root的祖先,如果不是就直接做,是的话应该查询从1-st[y ...

  9. BZOJ 3083 遥远的国度 树链剖分+线段树

    有换根的树链剖分的裸题. 在换根的时候注意讨论. 注意数据范围要开unsigned int或longlong #include<iostream> #include<cstdio&g ...

随机推荐

  1. AJAX 步骤分析

    HTML 步骤: 1. 创建异步对象 2. 设置请求行 3. 设置请求头(get请求可以省略) 4. 注册状态改变事件 4.1. 判断状态&请求是否成功并使用数据   5. 发送请求   PH ...

  2. 深入Spring:自定义IOC

    前言 上一篇文章讲了如何自定义注解,注解的加载和使用,这篇讲一下Spring的IOC过程,并通过自定义注解来实现IOC. 自定义注解 还是先看一下个最简单的例子,源码同样放在了Github. 先定义自 ...

  3. wxDateTime用法和转换成wxString

    转载别人的.void datetest() { wxDateTime now=wxDateTime::Now(); wxString date1=now.Format(); wxString date ...

  4. Linux 下安装sql server 时 2G内存限制的最新(2019-08-15) 解决方案

    关于 sqlserver 在linux下安装时有最小内存限制的问题,网上有很多类似的说明,那些操作都是正确的,如果不成功可能 “姿势”不对. 需要注意的是:不能使用最新版本!!!  不能在线下载!!! ...

  5. Spring MVC 配置Controller详解

    在SpringMVC中,对于Controller的配置方式有很多种,如下做简单总结 第一种 URL对应Bean如果要使用此类配置方式,需要在XML中做如下样式配置: <!-- 表示将请求的URL ...

  6. excel导出简单示例(jxl jar包)

    @param title excel文件名 @param excelTopName 表头中文名字(显示在第一行的中文表头内容) @param header 表头字段属性(根据该属性获取对应的属性值,表 ...

  7. Bootstrap 网页2

    html <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <me ...

  8. Jenkins配置定时构建项目

    general: 源码管理: 构建触发器: 构建后操作:

  9. Linux eth0, eth1, ..., eth%d 的生成【转】

    转自:https://blog.csdn.net/xiruanliuwei/article/details/78765255 一直很好奇,Linux下的eth0, eth1,eth2等是如何生成的~ ...

  10. UNP学习第七章

    一.套接口选项 函数getsockopt和setsockopt 函数fcntl 函数ioctl 二.getsockopt和setsockopt函数 #include <sys/socket.h& ...