树链剖分:

把一棵树剖分为若干条链,然后利用数据结构(树状数组,SBT,Splay,线段树等等)去维护每一条链,复杂度

为O(logn),总体复杂度O(nlog²n)

步骤:

①将树的边分成重边和轻边,令siz[u]为u节点所有子节点的个数,v是u所有儿子中siz[]值最大的,

那么边(u,v)就是重边,否则就是轻边

深搜一次就好,顺便求出每个节点的深度和每个节点的父亲是谁

数组:在第一次深搜时全部搜出

fa[u]      ------      u节点的父亲(根的父亲为0)

siz[u]      ------      u节点所有子节点的个数(叶子节点的siz[]值显然为0)

dep[u]      ------      u节点的深度(根的深度为1)

son[u]      ------      u节点的重儿子 [边(u,son[u])为重边]

②第二次深搜,将树分解成链,[重边->重链,轻边->将两条(个)重链或叶子连接在一起],

并求出每个节点所在链的顶端节点

深搜顺序:如果当前节点是重链上的节点,则随着这条重链往下搜,搜完回溯后再搜索与这个点相连的其他轻儿子

最后每个节点对应线段(链)中的编号就是搜索的dtime!

数组:在第二次搜索时全部搜出

top[u]      ------      u点所在链的顶端节点

rak[u]      ------     树中u节点剖分成链后对应的新编号

id[x]      ------      链中编号x的点对应的树的节点(反rak[])

经过前两次操作(两次搜索),树的剖分就完成了,树的问题就可以转化成链的问题

如上图:红色的边是重边,蓝色的边是轻边,红色的数字对应着当前节点在链中的编号,红点表示每个链的链头

性质:轻边(u,v)中,size(v)<=size(u/2)

从根到某一点的路径上,不超过logn条轻边和不超过logn条重边

对于每条重链,从链头到链尾编号从小到大

③:附录,例如这道题,如何修改某个节点u的值呢?

直接线段树单点更新,更新rak[u]点的值即可

如果查询两个节点u和v之间所有节点的和?

如果u和v在同一条重链中,那么好办,直接线段树区间查询一次即可,如果不在一条链中

就要让u点去找v点,不停地执行以下操作,让u点和v在同一条重链上,之后执行上个步骤

①u点爬到它所在重链的顶端节点,求出爬过的这段链的所有节点权值之和

②从该顶端节点爬向它的父亲(跑到另一条重链上了),之和继续执行步骤①

注意,如果每次u都是往上爬,深度是越来越低的!假设低过了v点的深度!那么u和v永远不可能碰面了,

所以每次爬的时候倒要比较下u和v的深度,让深度低的那个爬!!这也是为什么要求每个节点的深度

可能很难讲清楚,直接看代码好懂点

专门拉出来

int TreQuerys(int x, int y)      /*步骤③,模拟u和v相遇的过程,不停地线段树查询走过节点的权值和*/
{int sum, p1, p2;p1 = top[x], p2 = top[y], sum = 0;while(p1!=p2){if(dep[p1]<dep[p2])         /*让深度低的那个往上爬*/swap(p1, p2), swap(x, y);sum += Querys(1, n, 1, rak[p1], rak[x]);   /*普通线段树查询,下同*/x = fa[p1], p1 = top[x];     /*我要爬了*/}if(dep[x]>dep[y])swap(x, y);sum += Querys(1, n, 1, rak[x], rak[y]);return sum;
}

1036: [ZJOI2008]树的统计Count

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 16866  Solved: 6868
[Submit][Status][Discuss]

Description

  一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w。我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 III. QSUM u v: 询问从点u到点v的路径上的节点的权值和 注意:从点u到点v的路径上的节点包括u和v本身

Input

  输入的第一行为一个整数n,表示节点的个数。接下来n – 1行,每行2个整数a和b,表示节点a和节点b之间有一条边相连。接下来n行,每行一个整数,第i行的整数wi表示节点i的权值。接下来1行,为一个整数q,表示操作的总数。接下来q行,每行一个操作,以“CHANGE u t”或者“QMAX u v”或者“QSUM u v”的形式给出。 
对于100%的数据,保证1<=n<=30000,0<=q<=200000;中途操作中保证每个节点的权值w在-30000到30000之间。

Output

  对于每个“QMAX”或者“QSUM”的操作,每行输出一个整数表示要求输出的结果。

Sample Input

4
1 2
2 3
4 1
4 2 1 3
12
QMAX 3 4
QMAX 3 3
QMAX 3 2
QMAX 2 3
QSUM 3 4
QSUM 2 1
CHANGE 1 5
QMAX 3 4
CHANGE 3 6
QMAX 3 4
QMAX 2 4
QSUM 3 4

Sample Output

4
1
2
2
10
6
5
6
5
16

AC代码

#include<stdio.h>
#include<vector>
#include<string.h>
#include<algorithm>
using namespace std;
#define inf 2147483647;
vector<int> G[30005];
typedef struct
{int sum;int max;
}Tree;
Tree tre[125005];
int n, val[30005], son[30005], fa[30005], siz[30005], dep[30005];
int k, top[30005], rak[30005], id[30005];
void Sechs(int u, int p)        /*步骤①*/
{int v, i;fa[u] = p;dep[u] = dep[p]+1;for(i=0;i<G[u].size();i++){v = G[u][i];if(v==p)continue;Sechs(v, u);siz[u] += siz[v]+1;if(son[u]==0 || siz[v]>siz[son[u]])son[u] = v;}
}void Sechr(int u, int p)       /*步骤②*/
{int v, i;top[u] = p;rak[u] = ++k, id[k] = u;if(son[u]==0)return;Sechr(son[u], p);for(i=0;i<G[u].size();i++){v = G[u][i];if(v==son[u] || v==fa[u])continue;Sechr(v, v);}
}void Create(int l, int r, int x);                          /*四个线段树的简单操作*/
void Update(int l, int r, int x, int a, int b);
int Querys(int l, int r, int x, int a, int b);
int Queryx(int l, int r, int x, int a, int b);
int TreQuerys(int x, int y)     /*步骤③,模拟u和v相遇的过程,不停地线段树查询走过节点的权值和*/
{int sum, p1, p2;p1 = top[x], p2 = top[y], sum = 0;while(p1!=p2){if(dep[p1]<dep[p2])swap(p1, p2), swap(x, y);sum += Querys(1, n, 1, rak[p1], rak[x]);x = fa[p1], p1 = top[x];}if(dep[x]>dep[y])swap(x, y);sum += Querys(1, n, 1, rak[x], rak[y]);return sum;
}int TreQueryx(int x, int y)
{int now, p1, p2;p1 = top[x], p2 = top[y], now = -inf;while(p1!=p2){if(dep[p1]<dep[p2])swap(p1, p2), swap(x, y);now = max(now, Queryx(1, n, 1, rak[p1], rak[x]));x = fa[p1], p1 = top[x];}if(dep[x]>dep[y])swap(x, y);now = max(now, Queryx(1, n, 1, rak[x], rak[y]));return now;
}int main(void)
{int i, x, y, q;char str[15];while(scanf("%d", &n)!=EOF){for(i=1;i<=n;i++)G[i].clear();for(i=1;i<=n-1;i++){scanf("%d%d", &x, &y);G[x].push_back(y);G[y].push_back(x);}for(i=1;i<=n;i++)scanf("%d", &val[i]);memset(siz, 0, sizeof(siz));memset(son, 0, sizeof(son));k = 0;Sechs(1, 0);Sechr(1, 1);for(i=1;i<=n;i++)printf("%d ", rak[i]);scanf("%d", &q);Create(1, n, 1);while(q--){scanf("%s%d%d", str+1, &x, &y);if(str[1]=='C')Update(1, n, 1, rak[x], y);else if(str[2]=='M')printf("%d\n", TreQueryx(x, y));elseprintf("%d\n", TreQuerys(x, y));}}return 0;
}void Create(int l, int r, int x)
{int m;if(l==r){tre[x].sum = tre[x].max = val[id[l]];return;}m = (l+r)/2;Create(l, m, x*2);Create(m+1, r, x*2+1);tre[x].sum = tre[x*2].sum+tre[x*2+1].sum;tre[x].max = max(tre[x*2].max, tre[x*2+1].max);
}void Update(int l, int r, int x, int a, int b)
{int m;if(l==r && r==a){tre[x].max = tre[x].sum = b;return;}m = (l+r)/2;if(a<=m)  Update(l, m, x*2, a, b);else  Update(m+1, r, x*2+1, a, b);tre[x].sum = tre[x*2].sum+tre[x*2+1].sum;tre[x].max = max(tre[x*2].max, tre[x*2+1].max);
}int Querys(int l, int r, int x, int a, int b)
{int m, sum;if(l>=a && r<=b)return tre[x].sum;m = (l+r)/2;sum = 0;if(a<=m)  sum += Querys(l, m, x*2, a, b);if(b>=m+1)  sum += Querys(m+1, r, x*2+1, a, b);return sum;
}int Queryx(int l, int r, int x, int a, int b)
{int m, now;if(l>=a && r<=b)return tre[x].max;m = (l+r)/2;now = -inf;if(a<=m)  now = max(now, Queryx(l, m, x*2, a, b));if(b>=m+1)  now = max(now, Queryx(m+1, r, x*2+1, a, b));return now;
}

树链剖分(bzoj 1036: [ZJOI2008]树的统计Count)相关推荐

  1. 树链剖分 - BZOJ 1036: [ZJOI2008]树的统计Count

    这是树链剖分的入门题,也是我学树链剖分的第一题. 树链剖分:就是把树中和线段树联系起来,求(u,v)路径中权值的最大值和其路径的权值和. 入门blog:http://blog.sina.com.cn/ ...

  2. 【BZOJ4515】游戏,树链剖分+永久化标记线段树维护线段信息(李超线段树)

    Time:2016.05.10 Author:xiaoyimi 转载注明出处谢谢 传送门 思路: 李超线段树 一开始听faebdc讲,并没有听的很懂ww 后来找到良心博文啊有木有 折越 首先可以把修改 ...

  3. BZOJ4012[HNOI2015]开店——树链剖分+可持久化线段树/动态点分治+vector

    题目描述 风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到 人生哲学.最近她们灵机一动,打算在幻想乡开一家小店来做生意赚点钱.这样的 想法当然非常好啦,但是她们也发现她们面临着一个 ...

  4. BZOJ 1036: [ZJOI2008]树的统计Count

    1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MB Submit: 3427  Solved: 1429 [Submi ...

  5. 【树链剖分】「ZJOI2008」树的统计

    前置知识 树链剖分的思想及能解决的问题 树链剖分用于将树分割成若干条链的形式,以维护树上路径的信息. 具体来说,将整棵树剖分为若干条链,使它组合成线性结构,然后用其他的数据结构维护信息. 树链剖分(树 ...

  6. BZOJ 1036 [ZJOI2008]树的统计Count

    以前动态树写过这个题,今天尝试树链剖分解决~ 模板题,就声明一点,线段树维护的是点权 View Code 1 #include <iostream> 2 #include <cstd ...

  7. 计蒜客 - Distance on the tree(树链剖分+离线处理+线段树)

    题目链接:点击查看 题目大意:给出一颗含有n个节点的树,每条边都有权值,现在给出m个询问,每次询问的格式为u,v,w,我们需要求出在路径u-v上,边权小于等于w的边的个数 题目分析:因为一开始不会主席 ...

  8. 【树链剖分】洛谷树(P3401)

    正题 P3401 题目大意 给你一棵树,让你进行以下操作 修改一条边的边权 查询一条路径的所有子路径异或值的和 解题思路 记下所有点到根节点的路径亦或值,那么查询就是所有点对的异或值之和 因为边权&l ...

  9. 【树链剖分】【线段树】树的统计(金牌导航 树链剖分-1)

    树的统计 金牌导航 树链剖分-1 题目大意 给出一棵树,让你做若干操作,操作如下: 1.修改一个节点的值 2.查询两个节点之间路径的最大值 3.查询两个节点之间路径的和 输入样例 4 1 2 2 3 ...

最新文章

  1. 探索“小数”在计算机中的存储
  2. 阿里达摩院再造AI抗疫技术:20秒判读CT影像,识别准确率达96%
  3. 九大排序算法,你会几个?
  4. Windows配置本地域名
  5. 您需要了解的有关UI测试的所有信息
  6. 浮动的清除 -- 四种方法
  7. 一个关于mahout0.5放置位置的错误,,,
  8. 我的世界java出生蘑菇岛,《我的世界》出生就有蘑菇岛和海底神庙的地图,附近还有村庄!...
  9. linux嵌入式入门到精通视频教程 Linux开发工程师培训教程
  10. ISO18000-6B和ISO18000-6C(EPC C1 G2)标准的区别
  11. 联想笔记本键盘排线,联想笔记本原装键盘价格表 联想笔记本键盘如何更换
  12. android xutils3 注解,xUtils3使用简介
  13. centos 7下搭建wiki系统
  14. win10系统安装和优化
  15. 陕西二本计算机软件工程,高考分数不理想,仍然想报考软件工程专业,这四所二本大学不错...
  16. 从GraalVM到Quarkus系列-B002篇-Quarkus中的字节码框架gizmo
  17. 微型计算机的多级存储结构,微型计算机存储系统结构.doc
  18. 游戏图形测试软件,今日共有3款免费测试网络游戏(组图)
  19. 什么是CDN,内容分发网络学习
  20. c语言编程过程应注意什么意思,单片机C语言编程应注意的若干问题

热门文章

  1. python和java哪个好-Python和Java到底哪个更好?
  2. 2000坐标系转经纬度工具_【Leaflet开发】L.CRS + 搞定Leaflet多坐标系拓展
  3. erp服务器维护数据备份,erp服务器备份
  4. Hbuilder 屏幕放大缩小
  5. vue 后端返回图片乱码处理方法
  6. 蓝桥c++2013真题:逆波兰表达式(代码填空题)
  7. FFmpeg源代码简单分析:avio_open2()
  8. 小试ImageMagik——开发篇
  9. pandas 设置多重索引_pandas dataframe多重索引常用操作
  10. 安卓java摄像机的_在Android系统中调用系统前置摄像头