今天学习了树链剖分,记录一下。
【题目背景】
HYSBZ - 1036树的统计Count

【题目分析】
题目要求求任意结点之间路径的和以及路径上最大的结点,还有可能修改。如果正常做可能会很复杂(我也不知道正常应该怎么做,应该要用到LCA什么的,我还不太会)。
但是如果我们能够用线段树或者树状数组维护这个树,那么这种问题就会变得很简单。树链剖分就是这样一种将树映射在一个数组上变成线性结构然后用线段树进行维护的数据结构。
【基础知识】

  • 重儿子:儿子中子树结点数目最多的那个儿子(size最大)
  • 重边:父亲结点和重儿子连成的边
  • 重链:由多条重边连接而成的路径
  • 轻儿子:除了重儿子的其他儿子
  • 轻边:父亲和轻儿子连成的边
    如图所示,红圈的表示重儿子,黑边表示重边。由黑边组成的链为重链。

【具体实现】
我们先进行一次遍历得到重儿子以及深度等信息储存起来

void dfs1(int u,int f)
{int i,v;siz[u]=1; //储存该结点子树的大小(最小只有自身一个结点)son[u]=0;    //储存重儿子fa[u]=f;    //储存父节点h[u]=h[f]+1;//储存深度for(i=0;i<g[u].size();i++){v=g[u][i];if(v!=f){dfs1(v,u); //深度优先遍历siz[u]+=siz[v];if(siz[son[u]]<siz[v]) son[u]=v;}}
}

得到以上数据后,我们可以按重链将树映射在一个数组上。从根节点开始,优先将重链映射到数组上,然后按照深度依次进行轻儿子,轻儿子又是某一个重链的开始(每一个节点都处于一个且仅有一个重链中)。记录每条每个节点所属重链的开头(从而判断两个节点是否在同一个重链上)。

void dfs2(int u,int f,int k)
{int i,v;top[u]=k; //记录所属重链的开头pos[u]=++cnt;//映射到数组上的下标(同一个重链的下标是连续的)A[cnt]=val[u];//确定数组所对应节点的值方便进行维护if(son[u]) dfs2(son[u],u,k);//优先遍历重儿子,从而得到连续的重链for(i=0;i<g[u].size();i++){v=g[u][i];  if(v!=f&&v!=son[u]) dfs2(v,u,v);  //遍历其他轻儿子}
}

成功将树映射到数组上以后我们再用线段树对数组进行维护。对于线段树的维护是常规操作。

void update(int k,int l,int r,int x,int v)
{if(l==r){Sum[k]=Max[k]=v;return;}int mid=(l+r)/2;if(x<=mid) update(k<<1,l,mid,x,v);else update(k<<1|1,mid+1,r,x,v);Sum[k]=Sum[k<<1]+Sum[k<<1|1];Max[k]=max(Max[k<<1],Max[k<<1|1]);
}int QuerySum(int k,int l,int r,int L,int R)
{if(L<=l && r<=R) return Sum[k];int mid=(l+r)/2;int ret=0;if(L<=mid) ret+=QuerySum(k<<1,l,mid,L,R);if(R>mid) ret+=QuerySum(k<<1|1,mid+1,r,L,R);return ret;
}int QueryMax(int k,int l,int r,int L,int R)
{if(L==l && r==R) return Max[k];int mid=(l+r)/2;if(R<=mid) return QueryMax(k<<1,l,mid,L,R);else if(L>mid) return QueryMax(k<<1|1,mid+1,r,L,R);else return max(QueryMax(k<<1,l,mid,L,mid),QueryMax(k<<1|1,mid+1,r,mid+1,R));
}

重点还是对于树上两个点如何得到他们之间的一条路径以及这个路径在映射数组中的位置。我们每次从深度更深的点向上升,直到两个节点处在同一条链中(或者处于同一节点处)。在上升的过程中记录每条链的值(每条链都处于映射数组的一个连续的区间内)

int FindSum(int u,int v)
{int ans=0;while(top[u]!=top[v]){if(h[top[u]]<h[top[v]]) swap(u,v);ans+=QuerySum(1,1,n,pos[top[u]],pos[u]);u=fa[top[u]];}if(h[u]<h[v]) swap(u,v);ans+=QuerySum(1,1,n,pos[v],pos[u]);return ans;
}int FindMax(int u,int v)
{int ans=INT_MIN;while(top[u]!=top[v]){if(h[top[u]]<h[top[v]]) swap(u,v);ans=max(ans,QueryMax(1,1,n,pos[top[u]],pos[u]));u=fa[top[u]];}if(h[u]<h[v]) swap(u,v);ans=max(ans,QueryMax(1,1,n,pos[v],pos[u]));return ans;
}

这样我们就成功做到用线段树维护树状结构的数据啦
【AC代码】

#include<iostream>
#include<cstdio>
#include<vector>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<climits>using namespace std;const int MAXN=30010;
vector<int>g[MAXN];
int fa[MAXN],A[MAXN],val[MAXN],pos[MAXN],siz[MAXN],son[MAXN],h[MAXN],top[MAXN];
int cnt=0,n,m;
int Sum[MAXN<<2],Max[MAXN<<2];void dfs1(int u,int f)
{int i,v;siz[u]=1;son[u]=0;fa[u]=f;h[u]=h[f]+1;for(i=0;i<g[u].size();i++){v=g[u][i];if(v!=f){dfs1(v,u);siz[u]+=siz[v];if(siz[son[u]]<siz[v]) son[u]=v;}}
}
void dfs2(int u,int f,int k)
{int i,v;top[u]=k;pos[u]=++cnt;A[cnt]=val[u];if(son[u]) dfs2(son[u],u,k);for(i=0;i<g[u].size();i++){v=g[u][i];if(v!=f&&v!=son[u]) dfs2(v,u,v);}
}void update(int k,int l,int r,int x,int v)
{if(l==r){Sum[k]=Max[k]=v;return;}int mid=(l+r)/2;if(x<=mid) update(k<<1,l,mid,x,v);else update(k<<1|1,mid+1,r,x,v);Sum[k]=Sum[k<<1]+Sum[k<<1|1];Max[k]=max(Max[k<<1],Max[k<<1|1]);
}int QuerySum(int k,int l,int r,int L,int R)
{if(L<=l && r<=R) return Sum[k];int mid=(l+r)/2;int ret=0;if(L<=mid) ret+=QuerySum(k<<1,l,mid,L,R);if(R>mid) ret+=QuerySum(k<<1|1,mid+1,r,L,R);return ret;
}int QueryMax(int k,int l,int r,int L,int R)
{if(L==l && r==R) return Max[k];int mid=(l+r)/2;if(R<=mid) return QueryMax(k<<1,l,mid,L,R);else if(L>mid) return QueryMax(k<<1|1,mid+1,r,L,R);else return max(QueryMax(k<<1,l,mid,L,mid),QueryMax(k<<1|1,mid+1,r,mid+1,R));
}int FindSum(int u,int v)
{int ans=0;while(top[u]!=top[v]){if(h[top[u]]<h[top[v]]) swap(u,v);ans+=QuerySum(1,1,n,pos[top[u]],pos[u]);u=fa[top[u]];}if(h[u]<h[v]) swap(u,v);ans+=QuerySum(1,1,n,pos[v],pos[u]);return ans;
}int FindMax(int u,int v)
{int ans=INT_MIN;while(top[u]!=top[v]){if(h[top[u]]<h[top[v]]) swap(u,v);ans=max(ans,QueryMax(1,1,n,pos[top[u]],pos[u]));u=fa[top[u]];}if(h[u]<h[v]) swap(u,v);ans=max(ans,QueryMax(1,1,n,pos[v],pos[u]));return ans;
}int main()
{int a,b,i;char s[10];scanf("%d",&n);for(i=1;i<n;i++){scanf("%d%d",&a,&b);g[a].push_back(b);g[b].push_back(a);}for(i=1;i<=n;i++) scanf("%d",&val[i]);dfs1(1,0);dfs2(1,0,1);for(i=1;i<=n;i++) update(1,1,n,i,A[i]);scanf("%d",&m);while(m--){scanf("%s%d%d",s,&a,&b);if(s[1]=='H') update(1,1,n,pos[a],b);else if(s[1]=='S') printf("%d\n",FindSum(a,b));else printf("%d\n",FindMax(a,b));}return 0;
}

树链剖分入门+HYSBZ - 1036树的统计Count相关推荐

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

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

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

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

  3. 树链剖分入门——[kuangbin]树链剖分

    树链剖分的本质就是将一棵树拆分成一段一段连续的区间,然后放在一起就可以用一棵单独的线段树处理区间问题,只需要将树上节点和线段树节点的对应关系求好就可以很方便的互相转换,而树上两点之间路径的相关问题就可 ...

  4. 树链剖分(bzoj 1036: [ZJOI2008]树的统计Count)

    树链剖分: 把一棵树剖分为若干条链,然后利用数据结构(树状数组,SBT,Splay,线段树等等)去维护每一条链,复杂度 为O(logn),总体复杂度O(nlog²n) 步骤: ①将树的边分成重边和轻边 ...

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

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

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

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

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

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

  8. 【YBT2023寒假Day1 B】不跪模样(树链剖分)(线段树)

    不跪模样 题目链接:YBT2023寒假Day1 B 题目大意 给你一棵有根数,点有点权,两种操作: 对于所有 x 子树内与 x 距离不超过 2 的点,将其点权加 v. 询问 x 子树中,满足 i< ...

  9. 【GDOI2016】疯狂动物城(树链剖分+可持久化线段树)

    码农题- 调了我三个晚上- 看来我的代码能力还是太弱了- 首先我们不难发现在u到v这条链的答案为∑i=1n(n−i)(n−i+1)ai2\sum_{i=1}^n\frac{(n-i)(n-i+1)a_ ...

最新文章

  1. CPU/GPU/TPU/NPU...XPU都是什么意思?
  2. android 读取内部存储文件格式,Android中的数据储存之文件存储
  3. 裸centos安装PCRE时报错解决
  4. mysql自定义函数实现,自定义oracle中decode方法
  5. TopHQBooks – PDF 搜索引擎 - 小众软件
  6. 介绍10个常用的Python内置函数,99.99%的人都在用!
  7. docker之Dockerfile指令介绍
  8. 关于 SAP CRM 订单抬头级别的 Text 无法编辑的问题分析
  9. vc++ 项目里External dependencies 的文件存放内容和各个文件的存放内容
  10. 固定 顶部_优质的阳光板温室的顶部应该如此安装,专业的人做专业的事
  11. 机器学习笔记(三)——正则化最小二乘法
  12. Acrobat Reader DC 2019 for Mac(pdf文件阅读器) 中文直装
  13. 长假漫漫,不学门可视化图表课程提高下逼格?
  14. echarts引入china报错(The GeoJSON of the map must be provided)
  15. MATLAB中fspecial()函数的用法
  16. 为树莓派制作系统镜像时进行瘦身,方便后续保存与批量写入
  17. vba批量合并指定的sheet_用VBA实现把多个Excel文件合并到一个Excel文件的多个工作表(Sheet)里...
  18. CSI-RS接收流程
  19. Thread 1: signal SIGABRT解决方法之一
  20. matlab安装c盘吗,matlab的安装步骤(附winC盘“用户”文件夹下账户名的更改方法).doc...

热门文章

  1. pip3 install requests Cannot open D:\Python35\Scripts\pip3-script.py
  2. Thonny -- 简洁的 python 轻量级 IDE
  3. java IO(一):File类
  4. 004-JQuery属性
  5. Django 和 html
  6. 强肝保肝养肝4大食物
  7. 请不要对我说“你要马上把这个小问题修改好”
  8. 8-18-Exercise
  9. 使用DataTable更新数据库
  10. 金算盘高手论坛资料中心_3D006期 菜鸟论坛精英PK专栏 速来围观!!