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

如何求出树链:第一次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,w
dfs_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*];
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 = ;
memset(head, -, sizeof(head));
pos = ;
memset(son, -, 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] = ;
//遍历u的每个子节点
for(int i = head[u];i != -; i = edge[i].next){
int v = edge[i].to;
if (v != pre){
dfs1(v, u, d+);
num[u]+=num[v];
if (son[u]==-||num[v]>num[son[u]])
son[u] = v;
}
}
}
//第二次dfs求出top和p
void getpos(int u, int sp){
top[u] = sp;
if (son[u] != -){//如果不是叶子节点,必有重儿子
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 != -; 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*];
void build(int i, int l, int r){
segTree[i].l = l;
segTree[i].r = r;
segTree[i].Max = ;
if (l==r)
return;
int mid = l+r >> ;
build(i<<, l, mid);
build(i<<|, mid+, r);
}
void push_up(int i){
segTree[i].Max = max(segTree[i<<].Max, segTree[i<<|].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 >> ;
if (k <= mid)
update(i<<, k, val);
else
update(i<<|, 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>>;
if (r<=mid)
return query(i<<, l, r);
else if (l > mid)
return query(i<<|, l, r);
else
return max(query(i<<, l, mid), query(i<<|, mid+, r));
}
//查询u->v边的最大值
int find(int u, int v){
int f1 = top[u], f2 = top[v];
int tmp = ;
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(, 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(, p[son[u]], p[v]));
}
int e[MAXN][]; int main(){
int T;
int n;
scanf("%d", &T);
while(T--){
init();
scanf("%d", &n);
for(int i = ; i < n-; i++){
scanf("%d%d%d", &e[i][], &e[i][], &e[i][]);
addedge(e[i][], e[i][]);
addedge(e[i][], e[i][]);
}
dfs1(,,);
getpos(,);
build(,,pos-);
for(int i = ; i < n-; i++){
if (deep[e[i][]]>deep[e[i][]])
swap(e[i][], e[i][]);
update(, p[e[i][]], e[i][]);//把(u,v)的权值加入线段树
}
char op[];
int u, v;
while(scanf("%s", op)==){
if (op[]=='D')
break;
scanf("%d%d", &u, &v);
if (op[]=='Q')
cout << find(u, v)<<endl;
else
update(, p[e[u-][]], v);//修改第u条边,其下标就是u-1
}
}
return ;
}

简化后的代码

 /*
树链剖分:
将每一条重链在线段树上铺开,访问顺序越靠前的链越靠线段树左边
*/
#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<<];
int head[maxn],tot;
int top[maxn],fa[maxn],deep[maxn],num[maxn],p[maxn],fp[maxn],son[maxn],pos;
int init(){
tot=pos=;
memset(head,-,sizeof head);
memset(son,-,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]=;
for(int i=head[u];i!=-;i=edge[i].next){
int v=edge[i].to;
if(v==pre) continue;
dfs1(v,u,d+);
num[u]+=num[v];
if(son[u]==- || 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]==-) return;
else getpos(son[u],sp);
for(int i=head[u];i!=-;i=edge[i].next){
int v=edge[i].to;
if(v!=son[u] && v!=fa[u]) getpos(v,v);//重新开一条重链
}
}
int Max[maxn<<];
inline void pushup(int rt){Max[rt]=max(Max[rt<<],Max[rt<<|]);}
void build(int l,int r,int rt){
Max[rt]=;
if(l==r) return;
int m=l+r>>;
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>>;
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>>,res=-;
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=;
while(f1!=f2){
if(deep[f1]<deep[f2]){swap(f1,f2);swap(u,v);}
tmp=max(tmp,query(p[f1],p[u],,pos-,));
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],,pos-,));
}
int e[maxn<<][];//保存边
int main(){
int T,n;
cin >> T;
while(T--){
init();
scanf("%d",&n);
for(int i=;i<n-;i++){
scanf("%d%d%d",&e[i][],&e[i][],&e[i][]);
addedge(e[i][],e[i][]);addedge(e[i][],e[i][]);
}
dfs1(,,);getpos(,);build(,pos-,);
for(int i=;i<n-;i++){
if(deep[e[i][]]>deep[e[i][]]) swap(e[i][],e[i][]);
update(p[e[i][]],e[i][],,pos-,);//其实是把边权映射到点上,第一条边在线段树上对应的下标是1,根节点对应的虚边在线段树上下标是0,权值也是0
}
char op[];
int u,v;
while(scanf("%s",op)==){
if(op[]=='D') break;
scanf("%d%d",&u,&v);
if(op[]=='Q') printf("%d\n",find(u,v));
else update(p[e[u-][]],v,,pos-,);
}
}
return ;
}

树链剖分边权模板spoj375的更多相关文章

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

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

  2. 计蒜客 38229.Distance on the tree-1.树链剖分(边权)+可持久化线段树(区间小于等于k的数的个数)+离散化+离线处理 or 2.树上第k大(主席树)+二分+离散化+在线查询 (The Preliminary Contest for ICPC China Nanchang National Invitational 南昌邀请赛网络赛)

    Distance on the tree DSM(Data Structure Master) once learned about tree when he was preparing for NO ...

  3. BZOJ 1036 [ZJOI2008]树的统计Count (树链剖分 - 点权剖分 - 单点权修改)

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1036 树链剖分模版题,打的时候注意点就行.做这题的时候,真的傻了,单词拼错检查了一个多小时 ...

  4. POJ3237 Tree 树链剖分 边权

    POJ3237 Tree 树链剖分 边权 传送门:http://poj.org/problem?id=3237 题意: n个点的,n-1条边 修改单边边权 将a->b的边权取反 查询a-> ...

  5. POJ2763 Housewife Wind 树链剖分 边权

    POJ2763 Housewife Wind 树链剖分 边权 传送门:http://poj.org/problem?id=2763 题意: n个点的,n-1条边,有边权 修改单边边权 询问 输出 当前 ...

  6. HDU3669 Aragorn's Story 树链剖分 点权

    HDU3669 Aragorn's Story 树链剖分 点权 传送门:http://acm.hdu.edu.cn/showproblem.php?pid=3966 题意: n个点的,m条边,每个点都 ...

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

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

  8. BZOJ 1984: 月下“毛景树” [树链剖分 边权]

    1984: 月下“毛景树” Time Limit: 20 Sec  Memory Limit: 64 MBSubmit: 1728  Solved: 531[Submit][Status][Discu ...

  9. SPOJ 375 (树链剖分 - 边权剖分 - 修改单边权)

    题目链接:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=28982#problem/I 给你一棵有边权的树,有两个操作:一个操作是输出l到 ...

随机推荐

  1. 【CSS】定义元素的对齐方式

    1.文本内容居中对齐:text-align.扩展用法:父元素嵌套子元素时,且子元素的宽度小于父元素宽度,使用text-align:center,可以实现子元素的居中对齐. <!DOCTYPE h ...

  2. Scala进阶之路-Scala高级语法之隐式(implicit)详解

    Scala进阶之路-Scala高级语法之隐式(implicit)详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 我们调用别人的框架,发现少了一些方法,需要添加,但是让别人为你一 ...

  3. CodeChef - AMLEX-Poetic word

    题目链接  Dhinwaji is an acclaimed poet and likes to play with words and letters. He has bought some sti ...

  4. 淘淘商城之spring web mvc架构

    一.什么是springmvc springmvc是spring框架的一个模块,springmvc和spring无需通过中间整合层进行整合: springmvc是一个基于mvc的web框架   二.mv ...

  5. cpp与其他语言相比较

    new  相当于 malloc ,delete 相当于  free ,用法一样 c++ 有 namespace,可以避免类名污染  namespace xx{} c++ 有类 这个与 c#.as3比较 ...

  6. [CERC2016]机棚障碍 Hangar Hurdles(kruskal重构树+树上倍增)

    题面 \(solution:\) 某蒟蒻的心路历程: 这一题第一眼感觉很奇怪 带障碍物的图,最大的集装箱? 首先想到的就是限制我集装箱大小条件的是什么: 如果我要在某一个点上放一个集装箱且使它最大, ...

  7. [HEOI2015]定价 (贪心)

    分类讨论大法好! \(solution:\) 先说一下我对这个题目的态度: 首先这一题是贪心,这个十分明显,看了一眼其他题解都是十分优秀的贪心,可是大家都没有想过吗:你们贪心都是在区间\([l,r]\ ...

  8. JavaScript学习 - 基础(一)

    ECMAscript ECMAscript是一个重要的标准,但它并不是JAVAscript唯一的部分,当然,也不是唯一标准化的部分,实际上,一个完整的JAVAscript实现是由一下3个不同的部分组成 ...

  9. adb查看安卓设备系统Android版本

    adb shell getprop "ro.build.version" 结果中如下两项便是版本信息: [ro.build.version.release]: [4.4.4][ro ...

  10. 2018-2019-2 网络对抗技术 20165320 Exp2 后门原理与实践

    后门原理与实践 windows获取Linux操作Shell 获取本机的IP netcat介绍:一个进行基本的TCP.UDP数据收发的工具 相关的参数与具体用法 windows打开监听: ncat.ex ...