树链剖分

将一棵树的每个节点到它所有子节点中子树和(所包含的点的个数)最大的那个子节点的这条边标记为“重边”。

将其他的边标记为“轻边”。

若果一个非根节点的子树的大小不小于任意一个他兄弟节点的子数大小(若有多个就看心情选取其中的一个),那么它到它父节点的连边为重边,这个节点为重子节点,否则,它到它父节点的连边为轻边。

将一条全部由重边组成的链叫做重链。

(图中加粗的边为重边,未加粗的边为轻边,图取自www.baidu.com)

这样做有什么用呢?

如上图剖分后的树有这样的性质:

1.每个点都在一条重链中(轻子节点所在重链链顶是它本身)

2.每一条重链一定是自上而下(即不会再一条重链上出现两个深度相同的点)

3.任意一个节点到根节点的路径上最多有log2(n)条轻边和log2(n)条重链。

这样之后,我们可以按照优先级为“根节点>重子节点>轻子节点”的顺序进行两次dfs,O(n)预处理出dfs序,深度,每个节点所在重链的顶端,这样就能保证每一条重链中所有的点的dfs序中的位置都是自上而下连续而递增的。

然后,我们再用一个线段树O(log2(n))维护每一条重链和轻边上点的权值的单点修改,区间修改,区间和、区间最值之类的问题。

如果要处理路径上的问题,我们可以不断将两个点中所在重链链顶深度小的那个点直接跳到它所在重链链顶的父节点,并对这条重链进行操作(如果这个点是轻子节点那么就直接对这个点进行操作),直到这两个点到达了同一条重链,这是再将两个点之间的部分进行操作。

这样,我们每次完成对一条路径的操作或询问复杂度为log2(n)乘以 log2(m)(m为每条链的平均长度,实际上这个数通常较小)。

这里附上洛谷模板题和AC代码。

题目描述

如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作:

操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z

操作2: 格式: 2 x y 表示求树从x到y结点最短路径上所有节点的值之和

操作3: 格式: 3 x z 表示将以x为根节点的子树内所有节点值都加上z

操作4: 格式: 4 x 表示求以x为根节点的子树内所有节点值之和

输入输出格式

输入格式:

第一行包含4个正整数N、M、R、P,分别表示树的结点个数、操作个数、根节点序号和取模数(即所有的输出结果均对此取模)。

接下来一行包含N个非负整数,分别依次表示各个节点上初始的数值。

接下来N-1行每行包含两个整数x、y,表示点x和点y之间连有一条边(保证无环且连通)

接下来M行每行包含若干个正整数,每行表示一个操作,格式如下:

操作1: 1 x y z

操作2: 2 x y

操作3: 3 x z

操作4: 4 x

输出格式:

输出包含若干行,分别依次表示每个操作2或操作4所得的结果(对P取模)

输入输出样例

输入样例:

  1. 5 5 2 24
  2. 7 3 7 8 0
  3. 1 2
  4. 1 5
  5. 3 1
  6. 4 1
  7. 3 4 2
  8. 3 2 2
  9. 4 5
  10. 1 5 1 3
  11. 2 1 3
输出样例:

  1. 2
  2. 21

  1. #include<algorithm>
  2. #include<iostream>
  3. #include<cstring>
  4. #include<cstdio>
  5. #include<cmath>
  6. #define LL long long
  7. #define mid (l+r>>1)
  8. #define len (r-l+1)
  9. #define M 100100
  10. using namespace std;
  11. LL read(){
  12. LL nm=,oe=;char cw=getchar();
  13. while(!isdigit(cw)) oe=cw=='-'?-oe:oe,cw=getchar();
  14. while(isdigit(cw)) nm=nm*+(cw-''),cw=getchar();
  15. return nm*oe;
  16. }
  17. LL n,m,f[M],fa[M],nt[M<<],to[M<<],tp[M],d[M],w[M];
  18. LL cnt,cur,mod,rt,a,b,sz[M],gt[M],ed[M],s[M],num,c[M];
  19. LL t[M<<],mk[M<<],add,typ;
  20. bool fg[M];
  21. void link(){nt[++cur]=f[a],f[a]=cur,to[cur]=b;}
  22. void dfs1(LL x){
  23. sz[x]=;
  24. int sn=;
  25. for(int i=f[x];i!=-;i=nt[i]){
  26. if(to[i]==fa[x]) continue;
  27. fa[to[i]]=x,d[to[i]]=d[x]+;
  28. dfs1(to[i]),sz[x]+=sz[to[i]];
  29. if(sn==) sn=i;
  30. else if(sz[to[sn]]<sz[to[i]]) sn=i;
  31. }
  32. if(sn!=) swap(to[f[x]],to[sn]),fg[to[f[x]]]=true;
  33. return;
  34. }
  35. void dfs2(int x){
  36. if(fg[x]) tp[x]=tp[fa[x]];
  37. else tp[x]=x;
  38. gt[x]=++cnt,s[gt[x]]=x;
  39. for(int i=f[x];i!=-;i=nt[i]){
  40. if(to[i]==fa[x]) continue;
  41. dfs2(to[i]);
  42. }
  43. ed[x]=cnt;
  44. }
  45. int build(int x,int l,int r){
  46. if(l==r) return t[x]=w[s[l]];
  47. return t[x]=(build(x<<,l,mid)+build(x<<|,mid+,r))%mod;
  48. }
  49. void pushdown(int x,int l,int r){
  50. mk[x<<]+=mk[x],t[x<<]+=(mid-l+)*mk[x];
  51. mk[x<<|]+=mk[x],t[x<<|]+=(r-mid)*mk[x];
  52. mk[x]=;
  53. }
  54. void update(int x,int l,int r,int L,int R){
  55. if(r<L||l>R) return;
  56. if(L<=l&&r<=R){
  57. mk[x]+=add;
  58. t[x]+=len*add;
  59. t[x]%=mod;
  60. return;
  61. }
  62. pushdown(x,l,r);
  63. update(x<<,l,mid,L,R);
  64. update(x<<|,mid+,r,L,R);
  65. t[x]=(t[x<<]+t[x<<|])%mod;
  66. }
  67. LL calc(int x,int l,int r,int L,int R){
  68. if(r<L||R<l) return ;
  69. if(L<=l&&r<=R) return t[x];
  70. pushdown(x,l,r);
  71. LL tot=calc(x<<,l,mid,L,R)+calc(x<<|,mid+,r,L,R);
  72. t[x]=(t[x<<]+t[x<<|])%mod;
  73. return tot%mod;
  74. }
  75. void change(){
  76. int x=a,y=b;
  77. while(tp[x]!=tp[y]){
  78. if(d[tp[x]]<d[tp[y]]) swap(x,y);
  79. update(,,n,gt[tp[x]],gt[x]),x=fa[tp[x]];
  80. }
  81. if(d[x]>d[y]) swap(x,y);
  82. update(,,n,gt[x],gt[y]);
  83. return;
  84. }
  85. LL ans(){
  86. LL tot=0ll,x=a,y=b;
  87. while(tp[x]!=tp[y]){
  88. if(d[tp[x]]<d[tp[y]]) swap(x,y);
  89. tot+=calc(,,n,gt[tp[x]],gt[x]),x=fa[tp[x]];
  90. tot%=mod;
  91. }
  92. if(d[x]>d[y]) swap(x,y);
  93. tot+=calc(,,n,gt[x],gt[y]);
  94. return tot;
  95. }
  96. int main(){
  97. n=read(),m=read(),rt=read(),mod=read();
  98. for(int i=;i<=n;i++) w[i]=read(),f[i]=-,fg[i]=false;
  99. for(int i=;i<n;i++){
  100. a=read(),b=read();
  101. link(),swap(a,b),link();
  102. }
  103. tp[rt]=fa[rt]=rt,d[rt]=;
  104. dfs1(rt),dfs2(rt),build(,,n);
  105. while(m--){
  106. typ=read(),a=read();
  107. if(typ==) b=read(),add=read(),change();
  108. else if(typ==) b=read(),printf("%lld\n",ans()%mod);
  109. else if(typ==) add=read(),update(,,n,gt[a],ed[a]);
  110. else printf("%lld\n",calc(,,n,gt[a],ed[a])%mod);
  111. }
  112. return ;
  113. }

本人代码风格较为奇怪,请大家见谅。

洛谷 P3384 【模板】树链剖分的更多相关文章

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

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

  2. [luogu P3384] [模板]树链剖分

    [luogu P3384] [模板]树链剖分 题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点 ...

  3. 洛谷P3979 遥远的国度 树链剖分+分类讨论

    题意:给出一棵树,这棵树每个点有权值,然后有3种操作.操作一:修改树根为rt,操作二:修改u到v路径上点权值为w,操作三:询问以rt为根x子树的最小权值. 解法:如果没有修改树根操作那么这题就是树链剖 ...

  4. 洛谷 P4114 Qtree1 树链剖分

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

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

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

  6. 洛谷3384&bzoj1036树链剖分

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

  7. P3384 [模板] 树链剖分

    #include <bits/stdc++.h> using namespace std; typedef long long ll; int n, m, rt, mod, cnt, to ...

  8. luoguP3384 [模板]树链剖分

    luogu P3384 [模板]树链剖分 题目 #include<iostream> #include<cstdlib> #include<cstdio> #inc ...

  9. 【Luogu P3384】树链剖分模板

    树链剖分的基本思想是把一棵树剖分成若干条链,再利用线段树等数据结构维护相关数据,可以非常暴力优雅地解决很多问题. 树链剖分中的几个基本概念: 重儿子:对于当前节点的所有儿子中,子树大小最大的一个儿子就 ...

  10. 模板 树链剖分BFS版本

    //点和线段树都从1开始 //边使用vector vector<int> G[maxn]; ],num[maxn],iii[maxn],b[maxn],a[maxn],top[maxn], ...

随机推荐

  1. FPGA在其他领域的应用(二)

    计算机和存储领域: 计算机技术和存储技术发展迅猛.如今,云计算正在实现对传统 IT 功能和全新功能的整合.例如,许多大型数据中心目前正在同时提供传统的 IT 服务以及新型的数据分析服务. 因此,这些大 ...

  2. vue2购物车ch4-(筛选v-for 点击的那个设置样式 设为默认地址其他 联动 非循环的列表选中和非选中 删除当前选中的列表)

    1 address.html <!DOCTYPE html> <html lang="en"> <head> <meta charset= ...

  3. 【转】NAS群晖DSM 5.0-4458安装教程

    需要准备: 1.一个闲置的U盘,容量大于64M即可. 墙裂建议用如下U盘,可以隐藏成灰群晖,小巧方便. 闪迪(SanDisk)酷豆(CZ33)8GB U盘¥29.9京东商城    2.一台显示器,用于 ...

  4. KM算法的应用

    HDU2255 模板     难度x HDU2282 思维     难度XXx HDU3722 模板     难度X HDU3395 模版 HDU1533 最小值模型 难度x HDU2853 HDU3 ...

  5. Oracle之 any 、some、all解析

    oracle之 any.some.all 解析 因为很少用到, 所以几乎忘记了这几个函数, 不过它们还是很有用的使用它们可以大大简化一些SQL文的语法, 至于效率问题, 如CCW所说它们和EXISTS ...

  6. Win10系统下安装Ubuntu16.04.3教程与设置

    在Win10上刚刚装好Ubuntu16.04.3,装了不下于10次,期间出现很多问题,趁着还有记忆,写下这篇教程,里面还有Ubuntu系统的优化与Win10的一些设置. Part 1 制作Ubuntu ...

  7. Linux硬链接和软连接详解

    硬链接: 硬链接是通过索引节点inode来进行链接的(关于inode,http://www.cnblogs.com/ZGreMount/p/7653307.html).在Linux(ext2,ext3 ...

  8. Linux Redis集群搭建与集群客户端实现(Python)

    硬件环境 本文适用的硬件环境如下 Linux版本:CentOS release 6.7 (Final) Redis版本: Redis已经成功安装,安装路径为/home/idata/yangfan/lo ...

  9. vue-cli如何引入bootstrap工具

    以下操作以正常安装node环境为前提. 1.引入jq: 在npm控制台中,进入项目目录,然后输入指令npm install jquery --save-dev(npm换成cnpm更好,国内环境下使用c ...

  10. Iozone

    参考地址:iozone使用技巧.iozone和Fio安装测试说明 iozone介绍 iozone(www.iozone.org)是一个文件系统的benchmark工具,可以测试不同的操作系统中文件系统 ...