树链剖分边权模板spoj375
树链剖分是树分解成多条链来解决树上两点之间的路径上的问题
如何求出树链:第一次dfs求出树上每个结点的大小和深度和最大的儿子,第二次dfs就能将最大的儿子串起来并hash(映射)到线段树上(或者其他数据结构上),这就是一条重链。
一些性质:1.在树链上进行的算法要额外乘以一个logn:因为找u,v的lca复杂度为O(logn),在找lca的过程中进行其它算法操作,所以算总复杂度时要额外乘上logn
2.如果(v,u)为轻边,则siz[u] * 2 < siz[v]
3.从根到某一点的路径上轻链、重链的个数都不大于logn
4.可以沿着重链求出lca 每次求复杂度O(logn)
5.如果出现边权,那就将边权映射到对应的树节点上,根节点是没有被映射的
/* 树链剖分:在一棵树上进行路径的修改,求极值,求和 树链:树上的路径 剖分:把路径分为重链和轻链 siz[v]表示已v为根的子树的节点数,dep[v]表示v的深度(根深度为1) top[v]表示v所在的链的顶端结点,fa[v]表示v的父亲, son[v]表示与v在同一重链上的v的儿子节点(姑且称为重儿子) w[v]表示v与其父亲节点的连边(姑且称为v的父边)在线段树中的位置 重儿子:siz[u]为v的子节点中siz值最大的,那么u就是v的重儿子 轻儿子:v的其它子节点 重边:点v与其重儿子的连边 轻便:点v与其轻儿子的连边 重链:由重边构成的路径 轻链:轻边 性质1:(v,u)为轻边,则siz[u]*2<siz[v] 性质2:从根到某一点的路径上轻链,重链的个数都不大于logn算法:两个dfs求fa,dep,siz,son,top,wdfs_1:把fa,dep,siz,son求出来dfs_2:对于v,son[v]存在时(v不是叶子节点),显然有top[son[v]]==top[v]v的重边应当在v的父边的后面,记w[son[v]]=totw+1 ,totw表示最后加入的一条边在线段树中的位置此时,为了使一条重链各边在线段树中连续分布,应当进行dfs_2(son[v]);v的各个轻儿子u,显然有top[u]=u,并且w[u]=totw+1,进行dfs_2过程,这就求出了top和w 修改操作:将u到v的路径长得每条边的权值都加上某值x记 f1=top[u],f2=top[v]当 f1 != f2 :设dep[f1]>=dep[f2],更新u到f1的父边的权值(logn),并使u=fa[f1]当 f1 == f2 : u,v在同一条重链上,若u与v不是同一点,就更新u到v路径上的边的权值否则修改完成重复上述步骤 spoj375 */ #include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #define MAXN 10010 using namespace std; struct Edge{int to, next; }edge[MAXN*2]; int head[MAXN], tot; int top[MAXN];//所在的重链的顶端结点 int fa[MAXN];//父亲 int deep[MAXN];//深度 int num[MAXN];//子节点数 int p[MAXN];//p[v]表示v与其父亲节点的连边在线段树中的位置 int fp[MAXN];//和p数组相反 int son[MAXN];//重儿子 int pos;void init(){tot = 0;memset(head, -1, sizeof(head));pos = 0;memset(son, -1, sizeof(son)); } //链式前向星 void addedge(int u, int v){edge[tot].to = v;edge[tot].next = head[u];//下一条边存储的下标 head[u] = tot++; } //第一遍求出fa,deep,num,son void dfs1(int u, int pre, int d){deep[u] = d;fa[u] = pre;num[u] = 1;//遍历u的每个子节点 for(int i = head[u];i != -1; i = edge[i].next){int v = edge[i].to;if (v != pre){dfs1(v, u, d+1);num[u]+=num[v];if (son[u]==-1||num[v]>num[son[u]])son[u] = v; }} } //第二次dfs求出top和p void getpos(int u, int sp){top[u] = sp;if (son[u] != -1){//如果不是叶子节点,必有重儿子 p[u] = pos++; //表示u与父亲结点的连边在线段树上的位置 fp[p[u]] = u;getpos(son[u], sp);//顺着重链dfs }else {//叶子节点就不要dfs了 p[u] = pos++;fp[p[u]] = u;return;}for(int i = head[u]; i != -1; i = edge[i].next){//对于各个轻儿子 int v = edge[i].to;if (v != son[u] && v != fa[u])getpos(v, v);} } //线段树 struct Node{int l, r;int Max; }segTree[MAXN*3]; void build(int i, int l, int r){segTree[i].l = l;segTree[i].r = r;segTree[i].Max = 0;if (l==r)return;int mid = l+r >> 1;build(i<<1, l, mid);build(i<<1|1, mid+1, r); } void push_up(int i){segTree[i].Max = max(segTree[i<<1].Max, segTree[i<<1|1].Max); } void update(int i, int k, int val){//更新线段树的第k个值为val if(segTree[i].l == k && segTree[i].r == k){segTree[i].Max = val;return;} int mid = segTree[i].l+segTree[i].r >> 1;if (k <= mid)update(i<<1, k, val);elseupdate(i<<1|1, k, val);push_up(i); } //查询线段树中[l,r]的最大值,一条重链上的最大权值是很好查询的 int query(int i, int l, int r){if(segTree[i].l==l&&segTree[i].r==r)return segTree[i].Max;int mid = segTree[i].l+segTree[i].r>>1;if (r<=mid)return query(i<<1, l, r);else if (l > mid) return query(i<<1|1, l, r);else return max(query(i<<1, l, mid), query(i<<1|1, mid+1, r)); } //查询u->v边的最大值 int find(int u, int v){int f1 = top[u], f2 = top[v];int tmp = 0;while(f1 != f2){//两个点不是在同一重链上 if (deep[f1]<deep[f2]){swap(f1, f2);swap(u, v);}//默认deep[f1]比较大//当 f1 != f2 :设dep[f1]>=dep[f2],更新u到f1的父边的权值(logn),并使u=fa[f1]tmp = max(tmp, query(1, p[f1], p[u]));u = fa[f1];f1 = top[u]; }//最后一定会在同一条重链上 if (u==v)return tmp;if (deep[u]>deep[v])swap(u, v);return max(tmp, query(1, p[son[u]], p[v])); } int e[MAXN][3];int main(){int T;int n;scanf("%d", &T);while(T--){init();scanf("%d", &n);for(int i = 0; i < n-1; i++){scanf("%d%d%d", &e[i][0], &e[i][1], &e[i][2]);addedge(e[i][0], e[i][1]);addedge(e[i][1], e[i][0]);}dfs1(1,0,0);getpos(1,1);build(1,0,pos-1);for(int i = 0; i < n-1; i++){if (deep[e[i][0]]>deep[e[i][1]])swap(e[i][0], e[i][1]);update(1, p[e[i][1]], e[i][2]);//把(u,v)的权值加入线段树 } char op[10];int u, v;while(scanf("%s", op)==1){if (op[0]=='D')break;scanf("%d%d", &u, &v);if (op[0]=='Q')cout << find(u, v)<<endl;else update(1, p[e[u-1][1]], v);//修改第u条边,其下标就是u-1 }}return 0; }
简化后的代码
/* 树链剖分: 将每一条重链在线段树上铺开,访问顺序越靠前的链越靠线段树左边 */ #include<bits/stdc++.h> using namespace std; #define maxn 10010 #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 struct Edge{int to,next; }edge[maxn<<1]; int head[maxn],tot; int top[maxn],fa[maxn],deep[maxn],num[maxn],p[maxn],fp[maxn],son[maxn],pos; int init(){tot=pos=0;memset(head,-1,sizeof head);memset(son,-1,sizeof son); } void addedge(int u,int v){edge[tot].to=v;edge[tot].next=head[u];head[u]=tot++; } void dfs1(int u,int pre,int d){deep[u]=d;fa[u]=pre;num[u]=1;for(int i=head[u];i!=-1;i=edge[i].next){int v=edge[i].to;if(v==pre) continue;dfs1(v,u,d+1);num[u]+=num[v];if(son[u]==-1 || num[son[u]]<num[v]) son[u]=v;} } void getpos(int u,int sp){top[u]=sp;p[u]=pos++;fp[p[u]]=u;if(son[u]==-1) return;else getpos(son[u],sp);for(int i=head[u];i!=-1;i=edge[i].next){int v=edge[i].to;if(v!=son[u] && v!=fa[u]) getpos(v,v);//重新开一条重链 } } int Max[maxn<<2]; inline void pushup(int rt){Max[rt]=max(Max[rt<<1],Max[rt<<1|1]);} void build(int l,int r,int rt){Max[rt]=0;if(l==r) return;int m=l+r>>1;build(lson);build(rson); } void update(int pos,int val,int l,int r,int rt){if(l==r) {Max[rt]=val;return;}int m=l+r>>1;if(pos<=m) update(pos,val,lson);else update(pos,val,rson);pushup(rt); } int query(int L,int R,int l,int r,int rt){if(L<=l && R>=r) return Max[rt];int m=l+r>>1,res=-1;if(L<=m) res=max(res,query(L,R,lson));if(R>m) res=max(res,query(L,R,rson));return res; } int find(int u,int v){//查询u->v边上的最大值int f1=top[u],f2=top[v],tmp=0;while(f1!=f2){if(deep[f1]<deep[f2]){swap(f1,f2);swap(u,v);}tmp=max(tmp,query(p[f1],p[u],0,pos-1,1));u=fa[f1];f1=top[u];}if(u==v) return tmp;if(deep[u]>deep[v]) swap(u,v);return max(tmp,query(p[son[u]],p[v],0,pos-1,1)); } int e[maxn<<1][3];//保存边 int main(){int T,n;cin >> T;while(T--){init();scanf("%d",&n);for(int i=0;i<n-1;i++){scanf("%d%d%d",&e[i][0],&e[i][1],&e[i][2]);addedge(e[i][0],e[i][1]);addedge(e[i][1],e[i][0]);}dfs1(1,0,0);getpos(1,1);build(0,pos-1,1);for(int i=0;i<n-1;i++){if(deep[e[i][0]]>deep[e[i][1]]) swap(e[i][0],e[i][1]);update(p[e[i][1]],e[i][2],0,pos-1,1);//其实是把边权映射到点上,第一条边在线段树上对应的下标是1,根节点对应的虚边在线段树上下标是0,权值也是0 }char op[10];int u,v;while(scanf("%s",op)==1){if(op[0]=='D') break;scanf("%d%d",&u,&v);if(op[0]=='Q') printf("%d\n",find(u,v));else update(p[e[u-1][1]],v,0,pos-1,1);}}return 0; }
转载于:https://www.cnblogs.com/zsben991126/p/10039282.html
树链剖分边权模板spoj375相关推荐
- hdu3966 树链剖分点权模板+线段树区间更新/树状数组区间更新单点查询
点权树的模板题,另外发现树状数组也是可以区间更新的.. 注意在对链进行操作时方向不要搞错 线段树版本 #include<bits/stdc++.h> using namespace std ...
- 洛谷 P3384 【模板】树链剖分-树链剖分(点权)(路径节点更新、路径求和、子树节点更新、子树求和)模板-备注结合一下以前写的题目,懒得写很详细的注释...
P3384 [模板]树链剖分 题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节 ...
- hdu 3966( 树链剖分+点权更新)
题意:给一棵树,并给定各个点权的值,然后有3种操作: I C1 C2 K: 把C1与C2的路径上的所有点权值加上K D C1 C2 K:把C1与C2的路径上的所有点权值减去K Q C:查询节点编号为C ...
- 树链剖分概念及模板 + 例题 [POJ3237 tree + 软件包管理器]
文章目录 概念 模板 例题1:软件包管理器 题目 题解 代码实现 例题2:POJ3237 tree 题目 题解 代码实现 概念 树链剖分主要是用于解决以下这两个问题. 1.更改树上点x到点y的最短路径 ...
- POJ 3237.Tree -树链剖分(边权)(边值更新、路径边权最值、区间标记)贴个板子备忘...
Tree Time Limit: 5000MS Memory Limit: 131072K Total Submissions: 12247 Accepted: 3151 Descriptio ...
- 【树链剖分】【模板】树的统计(P2590)
小目录 链接 题目描述 样例输入 样例输出 思路 代码 链接 Luogu P2590 题目描述 给出一棵树,对该树进行单点修改,区间查询最大值,区间求和 样例输入 4 1 2 2 3 4 1 4 2 ...
- 树链剖分求lca模板
传说中比O(1)还快的求LCA的方法 再加上正向表优化, #include<cstdio> #include<cstring> #include<vector> u ...
- 【模板】树链剖分 P3384
题目链接 //部分转自:https://www.luogu.org/problemnew/solution/P3384 初学树链剖分,感觉这个模板题还是容易理解的,但是实在是码量很大的. 知识点: 重 ...
- BZOJ 2157 「国家集训队」旅游(树链剖分,线段树,边权转点权)【BZOJ计划】
整理的算法模板合集: ACM模板 点我看算法全家桶系列!!! 实际上是一个全新的精炼模板整合计划 题目链接 https://hydro.ac/d/bzoj/p/2157 是 hydro 的 BZOJ ...
最新文章
- Hadoop Backup Node
- R语言使用GGally包的ggparcoord函数可视化多变量的平行坐标轴图(parallel coordinates plot)、当排序点图的数据对象变多的可视化效果变差的时候
- RobotFramework教程使用笔记——requests和requestslibrary库
- android4.2添加重启菜单项
- SLAVE为什么一直不动了
- sql 问号的使用 php_PHP中bindParam和bindValue的区别
- 使用X Manager远程CentOS 7服务器(XDMCP)
- python 二次平滑_时序分析 指数平滑
- vscode 设置缩进为4_VS Code 设置缩进为4个空格
- shared_ptr简介以及常见问题
- aux 参数 linux,Linux下ps aux命令中STAT的参数含义(转)
- golang | windows平台golang环境搭建(过坑之后)
- phpexcel删除行_php - PHPExcel从大型工作表中删除行 - 堆栈内存溢出
- 职场风云 | 导师的光亮
- 细胞自动机 通用计算机,细胞自动机论
- 应用COMSOL Multiphysics分析水平井压裂裂缝应力干扰现象
- EXCEL对比重复数据
- python Numpy中的array函数讲解及各参数含义
- java获取本月最后一天
- 5操作系统的运行机制和体系结构