博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
树链剖分
阅读量:4476 次
发布时间:2019-06-08

本文共 4341 字,大约阅读时间需要 14 分钟。

  首先了解一下基本概念:

  重儿子:siz[u]为v的子节点中siz值最大的,那么u就是v的重儿子。

      轻儿子:v的其它子节点。
      重边:点v与其重儿子的连边。
      轻边:点v与其轻儿子的连边。
      重链:由重边连成的路径。
      轻链:轻边。
      剖分后的树有如下性质:
      性质1:如果(v,u)为轻边,则siz[u] * 2 < siz[v];
      性质2:从根到某一点的路径上轻链、重链的个数都不大于logN。

  树链剖分,如其字面意思,就是将一棵树按照轻重边剖分为多条链,使得每个节点都属于一条重链,然后按照某种顺序将链依次连接成一维结构,再通过线段树、树状数组、Splay等维护这条长链上的每一条链。

  由于重链的个数是O(logN)的,而每条链上的操作也是O(logN)的,故一次树上路径的操作的花费为O(log²N)。

  模板:

  

  题目输入输出要求:N个点,M(M=N-1)条边,K个操作

           N个点的值

           M行 (u, v)边

           K个操作,I (或D)a b c:a~b路径上的点值加(或减)c,Q a:a节点的值

 

1 #include
2 #include
3 #include
4 #include
5 #include
6 #include
7 #include
8 #pragma comment(linker, "/STACK:102400000,102400000") 9 using namespace std; 10 11 const int N = 50010, M = N * 2; 12 const int inf = 0x3f3f3f3f; 13 /*=============================================================*/ 14 /** 建树 */ 15 struct EG { 16 int u, v; 17 EG(){} 18 EG(int u, int v) : u(u), v(v) {} 19 } eg[M]; 20 int first[N], Next[M]; 21 int tot; 22 void init() 23 { 24 tot = 0; 25 memset(first, -1, sizeof(first)); 26 } 27 void add_edge(int u, int v) 28 { 29 eg[++tot] = EG(u, v); 30 Next[tot] = first[u], first[u] = tot; 31 eg[++tot] = EG(v, u); 32 Next[tot] = first[v], first[v] = tot; 33 } 34 35 /*=============================================================*/ 36 37 int fa[N], son[N], dep[N], dfn[N], top[N], sz[N], Rank[N], d[N]; 38 int tim; 39 /** 以DFS序遍历整棵树并获得如下信息: 40 sz[u] : 以u为根节点的子树的结点个数,是判断重儿子的依据 41 son[u]: 重儿子 42 dep[u]: u节点的深度 43 fa[u]: u节点的父节点 44 */ 45 void DFS1(int u) 46 { 47 sz[u] = 1; 48 son[u] = -1; 49 for(int i=first[u]; i!=-1; i=Next[i]) 50 { 51 EG &e = eg[i]; 52 if(e.v == fa[u]) continue; 53 dep[e.v] = dep[u] + 1; 54 fa[e.v] = u; 55 DFS1(e.v); 56 sz[u] += sz[e.v]; 57 if(son[u]==-1 || sz[son[u]] < sz[e.v]) 58 son[u] = e.v; 59 } 60 } 61 62 /** 63 将上面产生的重链串起来,生成Rank[]和dfn[]数组。 64 top[u]: u节点所在链的顶端节点 65 dfn[u]: u节点的DFS时间戳,各个重链是按照时间戳的先后形成的,也就是说,线段树、树状数组维护的数列是由时间戳从小到大一次形成的 66 Rank[t]: 时间戳为t的节点 67 */ 68 void DFS2(int u, int tp) 69 { 70 top[u] = tp; 71 dfn[u] = ++tim; 72 Rank[tim] = u; 73 if(son[u] == -1) return; 74 DFS2(son[u], tp); 75 for(int i=first[u]; i!=-1; i=Next[i]) 76 { 77 EG &e = eg[i]; 78 if(e.v != fa[u] && e.v != son[u]) 79 DFS2(e.v, e.v); 80 } 81 } 82 83 /** 线段树改段求点模板 */ 84 int mak[N<<2]; 85 void build(int l, int r, int rt) 86 { 87 mak[rt] = 0; 88 if(l == r) { 89 mak[rt] += d[Rank[l]]; 90 return; 91 } 92 int m = l + r >> 1; 93 build(l, m, rt<<1); 94 build(m+1, r, rt<<1|1); 95 } 96 97 void update(int L, int R, int a, int l, int r, int rt) 98 { 99 if(L <= l && R >= r) {100 mak[rt] += a;101 return;102 }103 int m = l + r >> 1;104 if(L <= m) update(L, R, a, l, m, rt<<1);105 if(R > m) update(L, R, a, m+1, r, rt<<1|1);106 }107 108 void query(int pos, int &a, int l, int r, int rt)109 {110 a += mak[rt];111 if(l == r)112 return;113 int m = l + r >> 1;114 if(pos <= m) query(pos, a, l, m, rt<<1);115 else query(pos, a, m+1, r, rt<<1|1);116 }117 118 /*==========================================================*/119 120 /** 操作树结构上在u、v路径上的节点*/121 void Update(int u, int v, int a, int n)122 {123 while(top[u] != top[v]) //当u、v节点不在同一条链上124 {125 //避免分类讨论,让u代表所在链顶较低的点126 if(dep[top[u]] < dep[top[v]]) swap(u, v);127 //top[u]~u这段路径在同一条重链上,其时间戳连续,更新之128 update(dfn[top[u]], dfn[u], a, 1, n, 1);129 130 //向上挑至链顶的父节点131 /*为什么是向上跳呢?132 因为v点所在链的顶端高于(或就是)u点所在链的顶端,无论哪种情况,v点所在链的顶端点的父节点在u~v链上*/133 u = fa[top[u]];134 }135 //此时,u、v在同一条链上,甚至u=v136 //重链上的节点,随着向子孙延伸,时间戳是递增的,即数列序是递增的137 if(dep[u] > dep[v]) swap(u, v);138 update(dfn[u], dfn[v], a, 1, n, 1);139 }140 141 /*==========================================================*/142 143 int main()144 {145 int n, m, k;146 while(scanf("%d %d %d", &n, &m, &k) == 3)147 {148 for(int i=1; i<=n; i++) scanf("%d", d+i);149 int a, b, c;150 init();151 for(int i=1; i<=m; i++)152 scanf("%d %d", &a, &b), add_edge(a,b);153 char s[3];154 dep[1] = 0;155 fa[1] = 1;156 tim = 0;157 DFS1(1);158 DFS2(1, 1);159 build(1, n, 1);160 while(k--)161 {162 scanf("%s", s);163 if(s[0] == 'Q') {164 scanf("%d", &a);165 b = 0;166 query(dfn[a], b, 1, n, 1);167 printf("%d\n", b);168 }169 else {170 scanf("%d %d %d", &a, &b, &c);171 Update(a, b, s[0]=='I' ? c : -c, n);172 }173 }174 }175 return 0;176 }
View Code

 

 

      

转载于:https://www.cnblogs.com/WCB-ACM/p/5245818.html

你可能感兴趣的文章
[LeetCode] SUM4
查看>>
安防摄像机手机直播方案介绍
查看>>
安防摄像头云端录像计划快捷配置-LiveNVR Onvif/RTSP流媒体服务
查看>>
包装类
查看>>
[转载]Spring学习4-面向切面(AOP)之aspectj注解方式
查看>>
201621123047 《Java程序设计》第10周学习总结
查看>>
no crontab for root 解决方案
查看>>
IAP 协议
查看>>
设计模式
查看>>
CentOS7修改网卡为eth0
查看>>
Best Quality CAT Caterpillar ET Diagnostic Adapter III
查看>>
定向转发和重定向实现 <select >下拉表单数据传送
查看>>
数组与字符串一(概念和基础)
查看>>
用cocos2d-html5做的消除类游戏《英雄爱消除》(4)——游戏结束
查看>>
CNUOJ 535 黑魔法师之门
查看>>
18. Maven 的单模块 / 多模块之 Spring MVC + Spring + Mybatis 项目讲解(重点)
查看>>
vs2017 F5不会自动编译了
查看>>
hdu 1028
查看>>
取消 Win7 驱动数字签名认证 WIN7 X64 系统
查看>>
East Brother Video Indoor Monitor
查看>>