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

如何求出树链:第一次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. my live bigdata

    s 上海移动 13585845289 张xx,安徽六安,下沙镇,沪南公路,X菇种植. 自然人严重失信黑名单查询 http://www.jscredit.gov.cn/credit/p/rb_list/ ...

  2. Word不能添加目录?

    我复制粘贴了一大把文字,标题也设置了,就是添加不了目录,也不知道是什么原因. 后来同事给我指点了一下,真是万分感激啊 比如下面这张图,第13章那么大的标题在那里,但是就是添加不了目录 原因是在于换行的 ...

  3. javascript 转换大小写字母

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  4. IoC之Ninject

    一.Ninject安装 Ninject是一个轻量级的开源的DI容器,可以通过Nuget直接安装: 二.Ninject的简单使用 模型代码: //计算器接口 public interface ICalc ...

  5. IdeaVim-常用操作

    IdeaVim简介 IdeaVim是IntelliJ IDEA的一款插件,他提高了我们写代码的速度,对代码的跳转,查找也很友好. 安装位置 安装之后它在 Tools > Vim Emulator ...

  6. Phalcon的Mvc结构及启动流程(部分源码分析)

    Phalcon本身有支持创建多种形式的Web应用项目以应对不同场景,包括迷你应用.单模块标准应用.以及较复杂的多模块应用 创建项目 Phalcon环境配置安装后,可以通过命令行生成一个标准的Phalc ...

  7. IntelliJ IDEA AndroidStudio SVN无法使用

    1.Cann't Run Program "SVN" 把勾都去掉,结果没有任何反应.2.重新安装TotoriseSVN,设置Svn.exe路径,主要不要勾选Enable Inter ...

  8. android BroadcastReceiver组件简单的使用

    1.清单文件 <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android=& ...

  9. 用winhotkey添加属于自己的快捷键

    需求 我要使用Win+N快捷键组合打开指定某个文件! 使用方法 打开winhotkey软件,做以下操作: 此刻,就可以用Win+N组合快捷键来打开指定目录了!

  10. k8s系列~mgr的应用

    一  简介:今天咱们大体介绍下 这两者是如何联系的二  概念解析     pod:说下我的理解    1 pod通过yaml文件来封装docker本身+启动形式    2 pod可以运行多个docke ...