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

如何求出树链:第一次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. python爬虫 bs4_4select()教程

    http://www.w3.org/TR/CSS2/selector.html 5 Selectors Contents 5.1 Pattern matching 5.2 Selector synta ...

  2. Hive记录-Hive调优

    1.Join优化 a.map join b.reduce join 小表为驱动表,或直接将小表加载到内存,做map端join,它的关键字为/*+MAP JOIN(t1)*/ 如果想自动开启map端Jo ...

  3. Linux下删除命令 硬盘空间查看... 常用命令

    (此命令请慎重使用) 使用rm -rf命令即可. 使用rm -rf 目录名字 命令即可 -r 就是向下递归,不管有多少级目录,一并删除-f 就是直接强行删除,不作任何提示的意思    (警告:不作任何 ...

  4. 自动提取文件系统---binwalk(一)

    Binwalk是路由器固件分析的必备工具,该工具最大的优点就是可以自动完成指定文件的扫描,智能发掘潜藏在文件中所有可疑的文件类型及文件系统. 1.Binwalk和libmagic Binwalk的扫描 ...

  5. Linux - 账户切换授权

    sudo 切换账户 echo myPassword | sudo -S ls /tmp # 直接输入sudo的密码非交互,从标准输入读取密码而不是终端设备 visudo # sudo命令权限添加 /e ...

  6. 高版本sonar安装遇到的坑-sonar 6.6

    最近安装了6.6版本的sonar,发现里面的坑还是很多,下面列举下遇到的坑 sonar插件地址:https://docs.sonarqube.org/display/PLUG/Plugin+Libra ...

  7. 第19月第8天 斯坦福大学公开课机器学习 (吴恩达 Andrew Ng)

    1.斯坦福大学公开课机器学习 (吴恩达 Andrew Ng) http://open.163.com/special/opencourse/machinelearning.html 笔记 http:/ ...

  8. MyEclipse2017 CI-7的破解

    下载了一个最新版的MyEclipse,网上下载了破解工具,按照步骤完成后破解失败.很纳闷,于是网上查看,说是破解器的版本须与MyEclipse的版本对应,不对应的话,是没有效果的.如我的是CI-7版本 ...

  9. 本体【Ontology】综述

    原文地址:http://blog.csdn.net/moonsheep_liu/article/details/22329873 本体作为一种能在语义和知识层次上描述领域概念的建模工具,其目标是捕获相 ...

  10. 常用的4个eclipse插件安装过程及使用方法

    最近整合了4个常用eclipse插件安装过程,分别是PMD.checkstyle.findbugs.sourcemonitor插件.因为我这里没有外网,所以所有的插件不是最新版,建议有网的童鞋自行在外 ...