树链剖分是树分解成多条链来解决树上两点之间的路径上的问题
如何求出树链:第一次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的更多相关文章
-
hdu3966 树链剖分点权模板+线段树区间更新/树状数组区间更新单点查询
点权树的模板题,另外发现树状数组也是可以区间更新的.. 注意在对链进行操作时方向不要搞错 线段树版本 #include<bits/stdc++.h> using namespace std ...
-
计蒜客 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 ...
-
BZOJ 1036 [ZJOI2008]树的统计Count (树链剖分 - 点权剖分 - 单点权修改)
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1036 树链剖分模版题,打的时候注意点就行.做这题的时候,真的傻了,单词拼错检查了一个多小时 ...
-
POJ3237 Tree 树链剖分 边权
POJ3237 Tree 树链剖分 边权 传送门:http://poj.org/problem?id=3237 题意: n个点的,n-1条边 修改单边边权 将a->b的边权取反 查询a-> ...
-
POJ2763 Housewife Wind 树链剖分 边权
POJ2763 Housewife Wind 树链剖分 边权 传送门:http://poj.org/problem?id=2763 题意: n个点的,n-1条边,有边权 修改单边边权 询问 输出 当前 ...
-
HDU3669 Aragorn&#39;s Story 树链剖分 点权
HDU3669 Aragorn's Story 树链剖分 点权 传送门:http://acm.hdu.edu.cn/showproblem.php?pid=3966 题意: n个点的,m条边,每个点都 ...
-
洛谷 P3384 【模板】树链剖分-树链剖分(点权)(路径节点更新、路径求和、子树节点更新、子树求和)模板-备注结合一下以前写的题目,懒得写很详细的注释
P3384 [模板]树链剖分 题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节 ...
-
BZOJ 1984: 月下“毛景树” [树链剖分 边权]
1984: 月下“毛景树” Time Limit: 20 Sec Memory Limit: 64 MBSubmit: 1728 Solved: 531[Submit][Status][Discu ...
-
SPOJ 375 (树链剖分 - 边权剖分 - 修改单边权)
题目链接:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=28982#problem/I 给你一棵有边权的树,有两个操作:一个操作是输出l到 ...
随机推荐
-
[Hadoop大数据]——Hive初识
Hive出现的背景 Hadoop提供了大数据的通用解决方案,比如存储提供了Hdfs,计算提供了MapReduce思想.但是想要写出MapReduce算法还是比较繁琐的,对于开发者来说,需要了解底层的h ...
-
Windows Azure Web Site (6) 使用FTP发布Azure Web Site
<Windows Azure Platform 系列文章目录> 笔者在之前的文章中介绍的都是使用IDE,也就是Visual Studio,将本地的aspx发布到Azure Web Site ...
-
C++ STL
1.stackstack 模板类的定义在<stack>头文件中.stack 模板类需要两个模板参数,一个是元素类型,一个容器类型,但只有元素类型是必要的,在不指定容器类型时,默认的容器类型 ...
-
web应用性能测试-Tomcat 7 连接数和线程数配置
转自:http://www.jianshu.com/p/8445645b3aff 引言 这段时间折腾了哈java web应用的压力测试,部署容器是tomcat 7.期间学到了蛮多散碎的知识点,及时梳理 ...
-
POJ 1664 放苹果 (递推)
题目链接:http://poj.org/problem?id=1664 dp[i][j]表示i个盘放j个苹果的方案数,dp[i][j] 可以由 dp[i - 1][j] 和 dp[i][j - i] ...
-
sizeof的用法的一些归纳
1 sizeof 是运算符,不是函数 2 sizeof 不能求得void类型的长度,能求得 void*类型的指针的长度 sizeof(void) 会导致编译错误.因为声明一个变量的最重要的作用就是告诉 ...
-
r语言之生成随机序列,随机数生成函数及用法
(1)生成正态分布随机数: rnorm(n,mean,sd) 其中,n表示生成的随机数个数,mean表示正态分布均值,sd表示正态分布标准差 > rnorm(5,0,2)[1] -5.3 ...
-
笔记:Hibernate 拦截器和事件
Hibernate 在执行持久化的过程中,应用程序通常无法参与其中,通过事件框架,Hibernate 允许应用程序能响应特定的内部事件,从而允许实现某些通用的功能,或者对 Hibernate 进行扩展 ...
-
Centos&;Redhat下bcm43142博通无线网卡linux驱动之二
上次通过更换内核实现成功编译驱动无线网卡,但是启动到系统原内核下依然没有bcm43142的驱动,遂准备在原内核下编译驱动,记录一下 ps:更推荐这种方法,避免因更换内核出现其他兼容性问题 1.准备驱动 ...
-
逆袭之旅DAY15.东软实训.Oracle.约束、序列、视图、索引、用户管理、角色
2018-07-11 08:26:00 有某个学生运动会比赛信息的数据库,保存了如下的表: 运动员sporter表:(运动员编号sporterid,运动员姓名name,运动员性别sex,所属系dep ...