题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=4010

题意;

先给你一棵树,有 \(4\) 种操作:

1、如果 \(x\) 和 \(y\) 不在同一棵树上则在\(x-y\)连边.

2、如果 \(x\) 和 \(y\) 在同一棵树上并且 \(x!=y\) 则把 \(x\) 换为树根并把 \(y\) 和 \(y\) 的父亲分离.

3、如果 \(x\) 和 \(y\) 在同一棵树上则 \(x\) 到 \(y\) 的路径上所有的点权值\(+w\).

4、如果 \(x\) 和 \(y\) 在同一棵树上则输出 \(x\) 到 \(y\) 路径上的最大值.

题解:

需要支持连边和删边等操作的,一般是动态树。

先把初始化的树建好,再套个 \(LCT\) 就可以了。可以说很裸了....但要注意题目中的一些细节。然而我还是调了挺久的...

\(LCT\) 的基本操作复杂度均摊是\(O(log^2n)\),不过常数可能大一点。

代码:

#include  <bits/stdc++.h>
using namespace std;
typedef long long ll; // 动态树问题,即要求我们维护一个由若干棵子结点无序的有根树组成的森林的连通性问题。
// 要求这个数据结构支持对树的分割、合并,对某个点到它到根的路径的某些操作
// LCT的基本操作复杂度为均摊O(log^2n). // http://acm.hdu.edu.cn/showproblem.php?pid=4010 const int N = 300100;
int n,m,val[N];
struct Link_Cut_Tree
{
int top,son[N][2],fa[N],q[N],rev[N];
int NodeVal[N]; // 每个点的权值
int MAX[N];
int ADD[N];
void init()
{
for(int i=0;i<=n;i++) {
son[i][0] = son[i][1] = 0;
}
top = 0;
memset(son,0,sizeof(son));
memset(fa,0,sizeof(fa));
memset(q,0,sizeof(q));
memset(rev,0,sizeof(rev));
memset(NodeVal,0,sizeof(NodeVal));
memset(MAX,0,sizeof(MAX));
memset(ADD,0,sizeof(ADD));
}
inline void pushup(int x) //上传Splay的最大值
{
MAX[x] = max(MAX[son[x][0]],MAX[son[x][1]]);
MAX[x] = max(MAX[x],NodeVal[x]);
}
void add(int x, int v)
{
NodeVal[x] += v;
MAX[x] += v;
ADD[x] += v;
}
inline void pushdown(int x) //下放Splay的翻转标记
{
int l=son[x][0],r=son[x][1];
if(rev[x]){
rev[l]^=1;rev[r]^=1;rev[x]^=1;
swap(son[x][0],son[x][1]);
}
if(ADD[x])
{
if(son[x][0]) {
add(son[x][0],ADD[x]);
}
if(son[x][1]) {
add(son[x][1],ADD[x]);
}
ADD[x] = 0;
}
}
// 判断这是不是一条重路径的根,只要他的fa指针指向的节点的左右子树都不是他,
// 证明此时这是一条虚边那么这就是一棵子树的根节点
inline bool isroot(int x) {
return !fa[x] || (son[fa[x]][0]!=x && son[fa[x]][1]!=x);
}
void rotate(int x) {
int y=fa[x],z=fa[y],l,r;
if(son[y][0]==x)l=0;
else l=1;
r=l^1;
if(!isroot(y)) {
if(son[z][0]==y) son[z][0]=x;
else son[z][1]=x;
}
fa[x]=z; fa[y]=x;
fa[son[x][r]]=y;
son[y][l]=son[x][r]; son[x][r]=y;
pushup(y); pushup(x);
}
// 提根
void splay(int x) {
top=1;q[top]=x;
for(int i=x;!isroot(i);i=fa[i]) q[++top]=fa[i];
for(int i=top;i;i--) pushdown(q[i]);
while(!isroot(x)) {
int y=fa[x],z=fa[y];
if(!isroot(y)) {
if((son[y][0]==x)^(son[z][0]==y)) rotate(x);
else rotate(y);
}
rotate(x);
}
}
// LCT核心:从当前的节点x向它所在的根节点连一条重路径,相当于把沿路的重路径全都断开,重新拉一条从x到根的重路径
void access(int x) {
for(int t=0;x;t=x,x=fa[x]) {
splay(x);
son[x][1]=t;
pushup(x);
}
}
//换根:让 x 成为当前树的根节点
void makeroot(int x) {
access(x);
splay(x);
rev[x]^=1;
}
// 先找到 x 所在的 auxiliary tree(即preferred path),并查找返回该条路径最小的节点(即根)
int find_root(int x) {
access(x);
splay(x);
while(son[x][0]) x=son[x][0];
return x;
}
//获取到 x−y所对应的路径,可以通过访问y节点来获取到有关路径的信息
void split(int x,int y) {
makeroot(x);
access(y);
splay(y);
}
// 断开 x 和 y 之间的边
void cut(int x,int y) {
split(x,y);
// if(son[y][0]==x && son[x][1]==0)
// {
// son[y][0]=0, fa[x]=0;
// pushup(y);
// }
fa[son[y][0]]=0;son[y][0]=0;
pushup(y);
}
// 在 x 和 y 之间连边
void link(int x,int y) {
makeroot(x);
// 注意有些题不保证x,y未联通,还需要判断一下联通性
// if(find_root(y)==x) return; //两点已经在同一子树中,再连边不合法
fa[x]=y;
}
// 将x-y路径上的权值都加 w
void add_val(int x,int y,int w)
{
split(x,y);
add(y,w);
}
// 输出x-y之间的最大点权
int query(int x, int y)
{
split(x,y);
return MAX[y];
}
}LCT;
std::vector<int> ve[N];
void dfs(int u, int fa)
{
LCT.fa[u] = fa;
for(int i = 0; i < (int)ve[u].size(); i++){
int v = ve[u][i];
if(v == fa)continue;
dfs(v,u);
}
}
int main(int argc, char const *argv[]) {
// freopen("in.txt","r",stdin);
while(std::cin >> n) { memset(val,0,sizeof(val));
for(int i = 0; i <= n; i++) ve[i].clear();
LCT.init();
for(int i = 1; i < n; i++) {
int u,v;
scanf("%d%d",&u,&v);
// LCT.link(u,v);
ve[u].push_back(v);
ve[v].push_back(u);
}
dfs(1,0);
for(int i = 1; i <= n; i++) {
int x;
scanf("%d", &x);
LCT.NodeVal[i] = x;
LCT.MAX[i] = x;
}
std::cin >> m;
for(int i = 1; i <= m; i++) {
int op, x, y;
scanf("%d", &op);
// 连接x到 y,若x到 y已经联通则无需连接
if(op == 1) {
scanf("%d%d",&x,&y);
int xx = LCT.find_root(x), yy = LCT.find_root(y);
if(xx != yy) LCT.link(x,y);
else {
puts("-1");
}
}
// 删除边(x,y),但不保证边(x,y)存在
else if(op == 2)
{
scanf("%d%d",&x,&y);
int xx = LCT.find_root(x), yy = LCT.find_root(y);
if(xx == yy && x != y) LCT.cut(x,y);
else {
puts("-1");
}
}
// 将x-y路径上的权值都加 w
else if(op == 3) {
int w;
scanf("%d%d%d",&w,&x,&y);
// std::cout << "w = " << w << '\n';
if(LCT.find_root(x) == LCT.find_root(y)) LCT.add_val(x,y,w);
else puts("-1"); }
// 输出x-y之间的最大点权
else if(op == 4) {
scanf("%d%d",&x,&y);
if(LCT.find_root(x) == LCT.find_root(y)) printf("%d\n",LCT.query(x,y));
else {
puts("-1");
}
}
}
puts("");
} return 0;
}

HDU 4010 Query on The Trees (动态树)(Link-Cut-Tree)的更多相关文章

  1. HDU 4010 Query on The Trees(动态树)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4010 题意:一棵树,四种操作: (1)若x和y不在一棵树上,将x和y连边: (2)若x和y在一棵树上, ...

  2. 动态树(Link Cut Tree) :SPOJ 375 Query on a tree

    QTREE - Query on a tree #number-theory You are given a tree (an acyclic undirected connected graph) ...

  3. 动态树(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 ...

  4. HDU 4010.Query on The Trees 解题报告

    题意: 给出一颗树,有4种操作: 1.如果x和y不在同一棵树上则在xy连边 2.如果x和y在同一棵树上并且x!=y则把x换为树根并把y和y的父亲分离 3.如果x和y在同一棵树上则x到y的路径上所有的点 ...

  5. 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 ...

  6. HDU 4010 Query on The Trees(动态树)

    题意 给定一棵 \(n\) 个节点的树,每个点有点权.完成 \(m\) 个操作,操作四两种,连接 \((x,y)\) :提 \(x\) 为根,并断 \(y\) 与它的父节点:增加路径 \((x,y)\ ...

  7. 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 ...

  8. hdu 4010 Query on The Trees LCT

    支持:1.添加边 x,y2.删边 x,y3.对于路径x,y上的所有节点的值加上w4.询问路径x,y上的所有节点的最大权值 分析:裸的lct...rev忘了清零死循环了两小时... 1:就是link操作 ...

  9. LCT总结——概念篇+洛谷P3690[模板]Link Cut Tree(动态树)(LCT,Splay)

    为了优化体验(其实是强迫症),蒟蒻把总结拆成了两篇,方便不同学习阶段的Dalao们切换. LCT总结--应用篇戳这里 概念.性质简述 首先介绍一下链剖分的概念(感谢laofu的讲课) 链剖分,是指一类 ...

随机推荐

  1. HttpService解析

    HttpServlet容器响应Web客户请求流程如下: 1)Web客户向Servlet容器发出Http请求: 2)Servlet容器解析Web客户的Http请求: 3)Servlet容器创建一个Htt ...

  2. 会变得ActionBar,让你的ActionBar与众不同

    话不多说先看两张图: github地址:https://github.com/Smalinuxer/android-SlideActionBar 原理什么的有时间再讲,或者自行看代码; 兴许还会补充新 ...

  3. 基于Linux的智能家居的设计(2)

    1  系统整体设计方案 智能家居系统的是一个实时查询家庭的温湿度.照明控制.自己主动控制的设定.集家庭娱乐.智能安防为一体,大量数据快处理.可靠的系统,因此在硬件和软件上都有非常大的要求,因此在这里进 ...

  4. (hdu step 7.2.1)The Euler function(欧拉函数模板题——求phi[a]到phi[b]的和)

    题目: The Euler function Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Othe ...

  5. [BZOJ5305][HAOI2018]苹果树 组合数学

    链接 小 C 在自己家的花园里种了一棵苹果树, 树上每个结点都有恰好两个分支. 经过细心的观察, 小 C 发现每一天这棵树都会生长出一个新的结点. 第一天的时候, 果树会长出一个根结点, 以后每一天, ...

  6. Linux下配置Squid基础教程

    Linux下配置Squid基础教程 本视频高清下载地址:http://down.51cto.com/data/437529 本文出自 "李晨光原创技术博客" 博客,请务必保留此出处 ...

  7. 安装、配置Vmware Esx Server 3.5视频全过程

    Vmware Esx server 的特点是它无需任何操作系统就可在硬件上运行,它的内核是VMware自己开发的VMkernel,可以理解成为Windows系统内核NTOSKRNL.另外它完全依靠Li ...

  8. Linux桌面词典 星际译王(StarDict)

    星际译王(StarDict)是利用GTK(GIMP TOOLKIT)开发的国际化的.跨平台的自由的桌面字典软件.它并不包含字典档,使用者须自行下载配合使用.它可以运行于多种不同的平台,如Linux, ...

  9. Microsoft office2016(专业增强版) 安装错误,报CRT(KB2999226)

    对着这个错误的出现,网上有解释,这里不多说(实际是我没有找到比较靠谱的说法..),跟Window Update这个服务有关. 首先打开”Windows人为管理器”->"服务" ...

  10. uni-app 地图初用 map

    一.uni-app 地图初用 map 代码如下: <template> <view> <!-- <page-head :title="title" ...