树链剖分是树分解成多条链来解决树上两点之间的路径上的问题

如何求出树链:第一次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相关推荐

  1. hdu3966 树链剖分点权模板+线段树区间更新/树状数组区间更新单点查询

    点权树的模板题,另外发现树状数组也是可以区间更新的.. 注意在对链进行操作时方向不要搞错 线段树版本 #include<bits/stdc++.h> using namespace std ...

  2. 洛谷 P3384 【模板】树链剖分-树链剖分(点权)(路径节点更新、路径求和、子树节点更新、子树求和)模板-备注结合一下以前写的题目,懒得写很详细的注释...

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

  3. hdu 3966( 树链剖分+点权更新)

    题意:给一棵树,并给定各个点权的值,然后有3种操作: I C1 C2 K: 把C1与C2的路径上的所有点权值加上K D C1 C2 K:把C1与C2的路径上的所有点权值减去K Q C:查询节点编号为C ...

  4. 树链剖分概念及模板 + 例题 [POJ3237 tree + 软件包管理器]

    文章目录 概念 模板 例题1:软件包管理器 题目 题解 代码实现 例题2:POJ3237 tree 题目 题解 代码实现 概念 树链剖分主要是用于解决以下这两个问题. 1.更改树上点x到点y的最短路径 ...

  5. POJ 3237.Tree -树链剖分(边权)(边值更新、路径边权最值、区间标记)贴个板子备忘...

    Tree Time Limit: 5000MS   Memory Limit: 131072K Total Submissions: 12247   Accepted: 3151 Descriptio ...

  6. 【树链剖分】【模板】树的统计(P2590)

    小目录 链接 题目描述 样例输入 样例输出 思路 代码 链接 Luogu P2590 题目描述 给出一棵树,对该树进行单点修改,区间查询最大值,区间求和 样例输入 4 1 2 2 3 4 1 4 2 ...

  7. 树链剖分求lca模板

    传说中比O(1)还快的求LCA的方法 再加上正向表优化, #include<cstdio> #include<cstring> #include<vector> u ...

  8. 【模板】树链剖分 P3384

    题目链接 //部分转自:https://www.luogu.org/problemnew/solution/P3384 初学树链剖分,感觉这个模板题还是容易理解的,但是实在是码量很大的. 知识点: 重 ...

  9. BZOJ 2157 「国家集训队」旅游(树链剖分,线段树,边权转点权)【BZOJ计划】

    整理的算法模板合集: ACM模板 点我看算法全家桶系列!!! 实际上是一个全新的精炼模板整合计划 题目链接 https://hydro.ac/d/bzoj/p/2157 是 hydro 的 BZOJ ...

最新文章

  1. Hadoop Backup Node
  2. R语言使用GGally包的ggparcoord函数可视化多变量的平行坐标轴图(parallel coordinates plot)、当排序点图的数据对象变多的可视化效果变差的时候
  3. RobotFramework教程使用笔记——requests和requestslibrary库
  4. android4.2添加重启菜单项
  5. SLAVE为什么一直不动了
  6. sql 问号的使用 php_PHP中bindParam和bindValue的区别
  7. 使用X Manager远程CentOS 7服务器(XDMCP)
  8. python 二次平滑_时序分析 指数平滑
  9. vscode 设置缩进为4_VS Code 设置缩进为4个空格
  10. shared_ptr简介以及常见问题
  11. aux 参数 linux,Linux下ps aux命令中STAT的参数含义(转)
  12. golang | windows平台golang环境搭建(过坑之后)
  13. phpexcel删除行_php - PHPExcel从大型工作表中删除行 - 堆栈内存溢出
  14. 职场风云 | 导师的光亮
  15. 细胞自动机 通用计算机,细胞自动机论
  16. 应用COMSOL Multiphysics分析水平井压裂裂缝应力干扰现象
  17. EXCEL对比重复数据
  18. python Numpy中的array函数讲解及各参数含义
  19. java获取本月最后一天
  20. 5操作系统的运行机制和体系结构

热门文章

  1. 计算机硬件只能做维修吗,关于计算机硬件维修的研究
  2. The Most Important Skill for Software Architects
  3. 育碧2k微软服务器,育碧服务器出现大规模的BUG:影响到多个平台
  4. 关于计算机工作的诗歌,提高计算机工作及上网效率的方法
  5. ES6 中的 Symbol 是什么?
  6. 解决CentOS 6 字体变成方框的方法
  7. 我的博客学习记录一箩筐(每天更新)
  8. flex图表坐标轴样式设置
  9. 讨论:多核CPU+ASIC的防火墙在以后是发展方向吗?
  10. lamda获取参数集合去空_lambda表达式对集合的遍历