SPOJ QTREE - Query on a tree 【树链剖分模板】
引用到的大佬博客
代码来自:http://blog.csdn.net/jinglinxiao/article/details/72940746
具体算法讲解来自:http://blog.sina.com.cn/s/blog_7a1746820100wp67.html
参考博客: http://www.cnblogs.com/barrier/p/6067964.html
http://www.cnblogs.com/sagitta/p/5660749.html
“在一棵树上进行路径的修改、求极值、求和”乍一看只要线段树就能轻松解决,实际上,仅凭线段树是不能搞定它的。我们需要用到一种貌似高级的复杂算法——树链剖分。
树链,就是树上的路径。剖分,就是把路径分类为重链和轻链。
记siz[v]表示以v为根的子树的节点数,dep[v]表示v的深度(根深度为1),top[v]表示v所在的链的顶端节点,fa[v]表示v的父亲,son[v]表示与v在同一重链上的v的儿子节点(姑且称为重儿子),w[v]表示v与其父亲节点的连边(姑且称为v的父边)在线段树中的位置。只要把这些东西求出来,就能用logn的时间完成原问题中的操作。
重儿子: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。
一般人需要先求LCA,然后慢慢修改u、v到公共祖先的边。而高手就不需要了。
记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路径上的边的权值(logn),否则修改完成;
重复上述过程,直到修改完成。
求和、求极值操作:类似修改操作,但是不更新边权,而是对其求和、求极值。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL; const int N=1e4+; int n;
int sz[N];
int fa[N]; //父节点
int son[N]; //重儿子
int d[N]; //深度
int in[N]; //in[i]记录结点i的dfs序 (优先搞定重儿子) ,这决定了该结点在线段树中映射到的位置
int top[N]; //记录结点i所在重链的起始结点
int time_tag; //时间戳
int lt[N]; //仅对重链的起始结点有效
vector<int> adj[N]; struct Edge
{
int u,v,c;
}es[N]; int tree[N<<]; void dfs1(int u,int father,int deep) //预处理出 fa[],sz[],d[],son[]
{
d[u]=deep;
sz[u]=;
fa[u]=father;
for(int v:adj[u])
{
if(v==father) continue;
dfs1(v,u,deep+);
sz[u]+=sz[v];
if(son[u]==-||sz[v]>sz[son[u]]) son[u]=v;
}
} void dfs2(int u) //预处理出in[],top[]
{
in[u]=++time_tag;
if(son[u]!=-) //存在重儿子
{
top[son[u]]=top[u];//同一重链上的top[]相同
dfs2(son[u]); //优先搞重儿子
}
for(int v:adj[u])
{
if(v==fa[u]||v==son[u]) continue;
top[v]=v; //开辟一条新的重链
dfs2(v);
}
} void update(int rt,int l,int r,int q,int val)
{
if(l==q&&r==q)
{
tree[rt]=val; //在线段树的叶子上,记录该结点与重儿子间的边权值
return ;
}
int mid=(l+r)>>;
if(q<=mid) update(rt<<,l,mid,q,val);
else update(rt<<|,mid+,r,q,val);
tree[rt]=max(tree[rt<<],tree[rt<<|]);
} int query(int rt,int l,int r,int ql,int qr) //只能对同一条重链上的两个点间的区间最大值进行查询
{
if(l>=ql&&r<=qr) return tree[rt];
int mid=(l+r)>>;
if(ql>mid) return query(rt<<|,mid+,r,ql,qr);
if(qr<=mid) return query(rt<<,l,mid,ql,qr);
return max(query(rt<<,l,mid,ql,qr),query(rt<<|,mid+,r,ql,qr));
} void init()
{
time_tag=;
memset(son,-,sizeof(son));
for(int i=;i<=n;i++) adj[i].clear();
top[]=;
} int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
init();
for(int i=;i<n;i++)
{
int u,v,c;
scanf("%d%d%d",&u,&v,&c);
es[i]=(Edge){u,v,c};
adj[u].push_back(v);
adj[v].push_back(u);
}
dfs1(,,);
dfs2();
for(int i=;i<n;i++)
{
int u=es[i].u,v=es[i].v;
if(top[u]==top[v]) //u,v在同一条重链上
{
if(d[u]>d[v]) swap(u,v); //使得u在上,v在下
update(,,n,in[u],es[i].c);
}
else //u,v不在同一条重链上
{
if(d[u]<d[v]) swap(u,v); //这样,u变成了一条新的重链的起始结点
lt[u]=es[i].c; //lt[u]表示u与父亲结点间的边权值
}
}
char s[];
while(~scanf("%s",s))
{
if(s[]=='D') break;
if(s[]=='Q')
{
int u,v;
scanf("%d%d",&u,&v);
int ans=;
while(top[u]!=top[v])
{
if(d[top[u]]<d[top[v]]) swap(u,v);
if(u!=top[u]) ans=max(ans,query(,,n,in[top[u]],in[u]-));
u=top[u];
ans=max(ans,lt[u]);
u=fa[u];
}
if(d[u]>d[v]) swap(u,v); //使得u在上,v在下,in[u]<in[v]
if(u!=v) ans=max(ans,query(,,n,in[u],in[v]-));
printf("%d\n",ans);
}
else
{
int x,c;
scanf("%d%d",&x,&c);
int u=es[x].u,v=es[x].v;
if(d[u]>d[v]) swap(u,v); //使得u在上,v在下
if(top[u]==top[v]) update(,,n,in[u],c);
else lt[v]=c;
}
}
}
}
SPOJ QTREE - Query on a tree 【树链剖分模板】的更多相关文章
- spoj QTREE - Query on a tree(树链剖分+线段树单点更新,区间查询)
传送门:Problem QTREE https://www.cnblogs.com/violet-acmer/p/9711441.html 题解: 树链剖分的模板题,看代码比看文字解析理解来的快~~~ ...
- SPOJ QTREE Query on a tree 树链剖分+线段树
题目链接:http://www.spoj.com/problems/QTREE/en/ QTREE - Query on a tree #tree You are given a tree (an a ...
- SPOJ QTREE Query on a tree ——树链剖分 线段树
[题目分析] 垃圾vjudge又挂了. 树链剖分裸题. 垃圾spoj,交了好几次,基本没改动却过了. [代码](自带常数,是别人的2倍左右) #include <cstdio> #incl ...
- SPOJ QTREE Query on a tree --树链剖分
题意:给一棵树,每次更新某条边或者查询u->v路径上的边权最大值. 解法:做过上一题,这题就没太大问题了,以终点的标号作为边的标号,因为dfs只能给点分配位置,而一棵树每条树边的终点只有一个. ...
- SPOJ 375 Query on a tree 树链剖分模板
第一次写树剖~ #include<iostream> #include<cstring> #include<cstdio> #define L(u) u<&l ...
- spoj 375 QTREE - Query on a tree 树链剖分
题目链接 给一棵树, 每条边有权值, 两种操作, 一种是将一条边的权值改变, 一种是询问u到v路径上最大的边的权值. 树链剖分模板. #include <iostream> #includ ...
- spoj 375 Query on a tree (树链剖分)
Query on a tree You are given a tree (an acyclic undirected connected graph) with N nodes, and edges ...
- Query on a tree 树链剖分 [模板]
You are given a tree (an acyclic undirected connected graph) with N nodes, and edges numbered 1, 2, ...
- Hdu 5274 Dylans loves tree (树链剖分模板)
Hdu 5274 Dylans loves tree (树链剖分模板) 题目传送门 #include <queue> #include <cmath> #include < ...
- SPOJ Query on a tree 树链剖分 水题
You are given a tree (an acyclic undirected connected graph) with N nodes, and edges numbered 1, 2, ...
随机推荐
- c# access oledb helper class
连接Access数据库 using System; using System.Collections.Generic; using System.Linq; using System.Text; us ...
- EDM设计案例分享:6款引人入胜的夏日邮件营销模板分享
夏日酷暑,清凉如风.在这个假期,旅游行业.酒店.服饰等都推出不少的假期活动,吸引游者的到来.假日期间,让我们看看一些旅游业.品牌服装店和酒店是怎么做好电子邮件广告的.在此,Focussend精心为大家 ...
- Delphi XE2 之 FireMonkey 入门(26) - 数据绑定: TBindingsList: TBindExprItems
Delphi XE2 之 FireMonkey 入门(26) - 数据绑定: TBindingsList: TBindExprItems 如果要给一对 "源控件" 和 " ...
- 网络编程之urllib
#网络爬虫,从其他的网站上,获取一些有用的内容,存入自己的数据库,然后再展示在指定的位置.#urllib是python自带的模块 1.urllib模块做网络爬虫,爬取网页: from urllib i ...
- dockerFile 配置puppeteer
## install npm && puppeteer## 必要依赖 libXScrnSaver RUN yum -y install libXScrnSaver ## install ...
- Python学习-第三天-面向对象编程基础
Python学习-第三天-面向对象编程基础 类和对象 简单的说,类是对象的蓝图和模板,而对象是类的实例.这个解释虽然有点像用概念在解释概念,但是从这句话我们至少可以看出,类是抽象的概念,而对象是具体的 ...
- 【Vue】通过自定义指令回顾 v-内置指令
Vue.js 的各种指令(Directives)更加方便我们去数据驱动 DOM,例如 v-bind.v-on.v-model.v-if.v-for.v-once 等内置指令,这些指令的职责就是当表达式 ...
- EA逆向生成数据库E-R图(mysql数据库-->ER图)
[1]选择 工具-->ODBC-Data-Sources [2]ODBC数据源管理器 ,点击添加 [3]选择一个mysql驱动 ,点击MySQL ODBC 5.1 Driver(其它同理), ...
- C# XML 解析包含特殊字符的内容
xml结构会解析一些特殊字符,特别是& < ,导致XmlDocument 解析错误 解决方法:将内容放在<![CDATA[ ]]>中,例如<![CDATA[2]] ...
- 提升JAVA代码的好“味道”
让代码性能更高 需要 Map 的主键和取值时,应该迭代 entrySet() 当循环中只需要 Map 的主键时,迭代 keySet() 是正确的.但是,当需要主键和取值时,迭代 entrySet() ...