HDU 4010 Query on The Trees(动态树)
题意
给定一棵 \(n\) 个节点的树,每个点有点权。完成 \(m\) 个操作,操作四两种,连接 \((x,y)\) ;提 \(x\) 为根,并断 \(y\) 与它的父节点;增加路径 \((x,y)\) 的节点一个 \(w\) 的点权;求路径 \((x,y)\) 的最大点权。
思路
基本概念介绍
\(\text{Link-Cut-Tree}\) 是一棵支持修改的树,以 \(\text{Splay}\) 为基础,均摊复杂度 \(O(n\log n)\) 的在线数据结构,支持的基础操作如下:
连边
断边
换根
路径修改
路径查询
无根树中,换根是用来进行路径操作的,而有根树并不能进行路径操作(因为需要改变根)
本题维护一棵无根树,使用以上功能即可。
如果用的够熟练,还有以下拓展功能:
- 有根树/基环树的处理
- 维护边权
- 维护最小生成树
- 维护子树信息
\(\text{LCT}\) 的结构是若干棵 \(\text{splay}\) ,每一棵 \(\text{splay}\) 维护的是原树上的某一条链(从顶到下)的一个序列,特殊的,某一棵 \(\text{splay}\) 根的父亲是它维护的实链在原树上对应的父亲,然而这个父亲并没有这个子节点(即认父不认子)。
基本函数
rotate
void rotate(int x)
{
int y=fa[x],z=fa[y],k=(x==ch[y][1]);
if(!isroot(y))ch[z][y==ch[z][1]]=x; fa[x]=z;
ch[y][k]=ch[x][!k]; if(ch[x][!k])fa[ch[x][!k]]=y;
ch[x][!k]=y,fa[y]=x;
push_up(y),push_up(x);
}
其中 \(\text{isroot}\) 函数如下:
bool isroot(int x){return x!=ch[fa[x]][0]&&x!=ch[fa[x]][1];}
功能是判断这个点是不是 \(\text{splay}\) 的根。
在 \(\text{splay}\) 里,上面的这些判断都可以略去,但是在 \(\text{LCT}\) 里不能略去。因为在 \(\text{LCT}\) 中,旋转是要在同一个 \(\text{splay}\) 中的,不能翻过头;而且零号节点有儿子可能会对 \(\text{isroot}\) 的判断有影响。
splay
void splay(int x)
{
stk[tp=1]=x;
for(int y=x;!isroot(y);y=fa[y])stk[++tp]=fa[y];
while(tp)push_down(stk[tp]),tp--;
while(!isroot(x))
{
int y=fa[x],z=fa[y];
if(!isroot(y))(x==ch[y][1])==(y==ch[z][1])?rotate(y):rotate(x);
rotate(x);
}
}
有两点不同,首先这里的 \(\text{splay}\) 函数只需要上旋至根,所以不用再传参数;然后是这个函数一般从底向上,即没有提前先找到要提到根的节点 \(x\) ,故需要提前 \(\text{down}\) 完一路上的节点。
access
\(\text{access}(x)\) 的作用是将从 \(x\) 节点到原树的根这条路径放在同一个 \(\text{splay}\) 中,即打通 \(x\) 到根的路径。
void access(int x)
{
for(int y=0;x;y=x,x=fa[x])
splay(x),ch[x][1]=y,push_up(x);
}
make_root
\(\text{make_root}(x)\) 用来将 \(x\) 提到根,它和上面的 \(\text{access}\) 都是 \(\text{LCT}\) 比较核心的部分,它的组成异常的简短。
void make_root(int x)
{
access(x),splay(x),reved(x);
}
\(\text{reved}\) 函数如下,表示旋转。
void reved(int x)
{
rev[x]^=1;
std::swap(ch[x][0],ch[x][1]);
}
将 \(x\) 与根放入同一个 \(\text{splay}\) 中,然后将 \(x\) 提到 \(\text{splay}\) 的根,由于然后对这个 \(\text{splay}\) 维护的序列进行翻转,不难发现,这样的旋转并不会使树的结构混乱,而恰好能将 \(x\) 作为根。
get_root
得到节点 \(x\) 的根,并将根也翻至 \(\text{splay}\) 的根。
int get_root(int x)
{
access(x),splay(x);
while(ch[x][0])push_down(x),x=ch[x][0];
splay(x);
return x;
}
link
\(\text{link}(x,y)\) 表示从 \(x\) 向 \(y\) 连一条边,如果 \(x,y\) 已经连通,则返回 \(\text{false}\) 。
bool link(int x,int y)
{
make_root(x);
if(get_root(y)==x)return false;
fa[x]=y;
return true;
}
cut
\(\text{cut}(x,y)\) 表示断开边 \((x,y)\),如果 \(x,y\) 没有连边,则返回 \(\text{false}\) 。
bool cut(int x,int y)
{
make_root(x);
if(get_root(y)!=x||ch[x][1]!=y||ch[y][0])return false; //画图观察,三个条件缺一不可
ch[x][1]=fa[y]=0;
push_up(x);
return true;
}
lift(split)
个人习惯把它叫作 \(\text{lift}\) ,其中 \(\text{lift}(x,y)\) 表示将路径 \((x,y)\) 实化,提起,以 \(x\) 作为根,如果 \((x,y)\) 不连通,则返回 \(\text{false}\)。
bool lift(int x,int y)
{
make_root(x),access(y);
return get_root(y)==x;
}
这是路径操作之前的一句话,完成后可以在 \(x\) 节点上打标记修改,\(x\) 节点也维护了路径的信息。
这个东西实在是个大坑,难以一文讲通,这里给出这道模板题的代码。
代码
#include<bits/stdc++.h>
#define FOR(i,x,y) for(int i=(x),i##END=(y);i<=i##END;++i)
#define DOR(i,x,y) for(int i=(x),i##END=(y);i>=i##END;--i)
template<typename T,typename _T>inline bool chk_min(T &x,const _T y){return y<x?x=y,1:0;}
template<typename T,typename _T>inline bool chk_max(T &x,const _T y){return x<y?x=y,1:0;}
typedef long long ll;
const int N=3e5+5;
struct LinkCutTree
{
int ch[N][2],fa[N];bool rev[N];
int pw[N],Mx[N],tag[N];
int stk[N],tp;
void init(){create(0,0);}
void create(int x,int val)
{
ch[x][0]=ch[x][1]=fa[x]=rev[x]=0;
tag[x]=0;pw[x]=Mx[x]=val;
}
bool isroot(int x){return x!=ch[fa[x]][0]&&x!=ch[fa[x]][1];}
void reved(int x)
{
rev[x]^=1;
std::swap(ch[x][0],ch[x][1]);
}
void taged(int x,int val)
{
tag[x]+=val;
pw[x]+=val;
Mx[x]+=val;
}
void push_up(int x)
{
Mx[x]=std::max(std::max(Mx[ch[x][0]],Mx[ch[x][1]]),pw[x]);
}
void push_down(int x)
{
if(rev[x])
{
if(ch[x][0])reved(ch[x][0]);
if(ch[x][1])reved(ch[x][1]);
rev[x]=0;
}
if(tag[x])
{
if(ch[x][0])taged(ch[x][0],tag[x]);
if(ch[x][1])taged(ch[x][1],tag[x]);
tag[x]=0;
}
}
void rotate(int x)
{
int y=fa[x],z=fa[y],k=(x==ch[y][1]);
if(!isroot(y))ch[z][y==ch[z][1]]=x; fa[x]=z;
ch[y][k]=ch[x][!k]; if(ch[x][!k])fa[ch[x][!k]]=y;
ch[x][!k]=y,fa[y]=x;
push_up(y),push_up(x);
}
void splay(int x)
{
stk[tp=1]=x;
for(int y=x;!isroot(y);y=fa[y])stk[++tp]=fa[y];
while(tp)push_down(stk[tp]),tp--;
while(!isroot(x))
{
int y=fa[x],z=fa[y];
if(!isroot(y))(x==ch[y][1])==(y==ch[z][1])?rotate(y):rotate(x);
rotate(x);
}
}
void access(int x)
{
for(int y=0;x;y=x,x=fa[x])
splay(x),ch[x][1]=y,push_up(x);
}
void make_root(int x)
{
access(x),splay(x),reved(x);
}
int get_root(int x)
{
access(x),splay(x);
while(ch[x][0])push_down(x),x=ch[x][0];
splay(x);
return x;
}
int get_fa(int x)
{
access(x),splay(x);
if(!ch[x][0])return -1;
push_down(x);
x=ch[x][0];
while(ch[x][1])push_down(x),x=ch[x][1];
splay(x);
return x;
}
bool link(int x,int y)
{
make_root(x);
if(get_root(y)==x)return false;
fa[x]=y;
return true;
}
bool cut(int x,int y)
{
make_root(x);
if(get_root(y)!=x||ch[x][1]!=y||ch[y][0])return false;
ch[x][1]=fa[y]=0;
push_up(x);
return true;
}
bool lift(int x,int y)
{
make_root(x),access(y);
return get_root(y)==x;
}
void update(int x,int y,int val)
{
lift(x,y);
taged(x,val);
}
int query(int x,int y)
{
lift(x,y);
return Mx[x];
}
}LCT;
int n,m;
int main()
{
while(~scanf("%d",&n))
{
LCT.init();
FOR(i,1,n)LCT.create(i,0);
FOR(i,1,n-1)
{
int u,v;
scanf("%d%d",&u,&v);
LCT.link(u,v);
}
FOR(i,1,n)
{
int x;
scanf("%d",&x);
LCT.update(i,i,x);
}
scanf("%d",&m);
while(m--)
{
int op,x,y,z;
scanf("%d",&op);
if(op==1)
{
scanf("%d%d",&x,&y);
if(!LCT.link(x,y))puts("-1");
}
else if(op==2)
{
scanf("%d%d",&x,&y);
if(x==y||!LCT.lift(x,y))puts("-1");
else
{
LCT.make_root(x);
LCT.cut(y,LCT.get_fa(y));
}
}
else if(op==3)
{
scanf("%d%d%d",&z,&x,&y);
if(!LCT.lift(x,y))puts("-1");
else LCT.update(x,y,z);
}
else if(op==4)
{
scanf("%d%d",&x,&y);
if(!LCT.lift(x,y))puts("-1");
else printf("%d\n",LCT.query(x,y));
}
}
puts("");
}
return 0;
}
HDU 4010 Query on The Trees(动态树)的更多相关文章
- HDU 4010 Query on The Trees (动态树)(Link-Cut-Tree)
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=4010 题意; 先给你一棵树,有 \(4\) 种操作: 1.如果 \(x\) 和 \(y\) 不在同一 ...
- HDU 4010 Query on The Trees(动态树)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4010 题意:一棵树,四种操作: (1)若x和y不在一棵树上,将x和y连边: (2)若x和y在一棵树上, ...
- 动态树(LCT):HDU 4010 Query on The Trees
Query on The Trees Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65768/65768 K (Java/Othe ...
- HDU 4010.Query on The Trees 解题报告
题意: 给出一颗树,有4种操作: 1.如果x和y不在同一棵树上则在xy连边 2.如果x和y在同一棵树上并且x!=y则把x换为树根并把y和y的父亲分离 3.如果x和y在同一棵树上则x到y的路径上所有的点 ...
- HDU 4010 Query on The Trees(动态树LCT)
Problem Description We have met so many problems on the tree, so today we will have a query problem ...
- HDU 4010 Query on The Trees
Problem Description We have met so many problems on the tree, so today we will have a query problem ...
- hdu 4010 Query on The Trees LCT
支持:1.添加边 x,y2.删边 x,y3.对于路径x,y上的所有节点的值加上w4.询问路径x,y上的所有节点的最大权值 分析:裸的lct...rev忘了清零死循环了两小时... 1:就是link操作 ...
- HDOJ 4010 Query on The Trees LCT
LCT: 分割.合并子树,路径上全部点的点权添加一个值,查询路径上点权的最大值 Query on The Trees Time Limit: 10000/5000 MS (Java/Others) ...
- Hdu 4010-Query on The Trees LCT,动态树
Query on The Trees Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65768/65768 K (Java/Othe ...
随机推荐
- javascript的数组之sort()
sort()方法用in-place的算法对原数组进行排序,但不会产生新的数组.这个方法不是一个稳定的排序,默认采用的是安字符串Unicode码点进行排序的. let fruit = ['cherri ...
- 使用强类型DataSet增加数据并获取自动增长的ID
使用强类型的DataSet可以方便的操作数据库:有时候我们会对表的ID设置为自动增长,并且需要在插入数据后获取新插入数据的ID,按以下方法即可达到目的: 一. 首先建立一个表,id为自动增加, ...
- Web开发——HTML基础(文件和网站结构)
参考: 参考:Document and website structure 参考:使用HTML部分和大纲 目录: 1.基本部分 2.用于构造内容的HTML 编辑 2.1 主动学习:探索我们的例子的代码 ...
- Xcode工程编译错误之iOS开发之Sending '__strong typeof (xxx)' (aka 'xxxx *__strong') to parameter of incompatible type 'id<xxx>'
iphone开发出现警告: Sending '__strong typeof (xxx)' (aka 'xxxx *__strong') to parameter of incompatible ty ...
- python摸爬滚打之day26----网络编程之socket
1.网络通信原理 互联网的本质就是一系列的网络协议, 统称为互联网协议. 互联网协议的功能:定义计算机如何接入internet,以及接入internet的计算机通信的标准. 互联网协议按照功能不同分为 ...
- ES6 迭代器
Iterator Iterator 是 ES6 引入的一种新的遍历机制,迭代器有两个核心概念: 迭代器是一个统一的接口,它的作用是使各种数据结构可被便捷的访问,它是通过一个键为Symbol.itera ...
- echart tootip使用技巧
1.关于混合折线图tooltip显示不同单位 formatter: function (params){ return params[0].name + '<br/>' + params ...
- C++提供的四种新式转换--const_cast dynamic_cast reinterpret_cast static_cast
关于强制类型转换的问题,许多书都讨论过,写的最具体的是C++之父的<C++的设计和演化>. 最好的解决方法就是不要使用C风格的强制类型转换,而是使用标准C++的类型转换符:static_c ...
- XP支持AHCI硬盘工作模式
故障 装XP系统后开启AHCI模式会出现开机蓝屏重启的问题,如何在XP下加载AHCI驱动,以便开启BIOS中AHCI选项来发挥硬盘的最佳性能. 问题分析XP系统无法直接支持AHCI硬盘高速模式,需要加 ...
- java框架之Struts2(1)-简介及入门
简介 Struts2 是一个基于 MVC 设计模式的 Web 应用框架,它本质上相当于一个 servlet,在 MVC 设计模式中,Struts2 作为控制器 (Controller) 来建立模型与视 ...