数据结构&图论:LCT
HDU4010
类比静态区间问题->动态区间问题的拓展
我们这里把区间变成树,树上的写改删查问题,最最最常用LCT解决
LCT用来维护动态的森林,对于森林中的每一棵树,用Splay维护。
LCT是把这些Splay关联在一起的数据结构
我们以HDU4010为例子
int n,m,cnt,top;
bool rev[maxn];
int mx[maxn],fa[maxn],v[maxn],tag[maxn],last[maxn],q[maxn];
int c[maxn][];
struct edge{int to,next;}e[maxn<<];
这里把树存成了图,邻接表表示,对于森林中的每一棵树,用Splay来维护
mx,fa,v,tag,c和Splay相关,q是个栈用来预处理fa还有协助Splay操作(具体问题请参考本站Splay的那篇介绍)
这里树边为双向边,在遍历的时候注意判断
接下来介绍LCT中的一些概念和实现:
void access(int x)
{
for(int t=;x;t=x,x=fa[x])
splay(x),c[x][]=t,update(x);
}
这个access的意思是专门开辟一条从根到x的路径,将其作为“重链”,并使用Splay来进行维护
如果x往下是重边,就将其变成轻边,这样这条重链就独立出来了
void makeroot(int x)
{
access(x);splay(x);rev[x]^=;
}
这个makeroot的意思是把某一个节点变成整个LCT的根,这个操作配合access操作就可以方便地提取LCT任意两点之间的路径了
void link(int x,int y)
{
makeroot(x);fa[x]=y;
}
Link-Cut Tree中的link,这个的意思就是连接两棵LCT
void cut(int x,int y)
{
makeroot(x);access(y);splay(y);
c[y][]=fa[c[y][]]=;update(y);
}
这个的意思就是把一棵LCT分离成两棵LCT
还有一个find函数:
int find(int x)
{
access(x);splay(x);
while(c[x][]) x=c[x][];
return x;
}
作用类似于并查集,用来判断一个点到底在哪棵LCT上面
还有一个操作是把一条路径的点权增大val
void add(int x,int y,int val)
{
makeroot(x);access(y);splay(y);
tag[y]+=val;mx[y]+=val;v[y]+=val;
}
最后给出求路径上最值的方法:
makeroot(x);access(y);splay(y);printf("%d\n",mx[y]);
先把x提取到LCT的根,然后打通到y的路径,然后把y伸展到根直接查询mx[y]即可
我们在进行题目所描述的一系列操作的时候,是需要前提的
link操作只能连接两棵不同的LCT,否则树就不是树了
cut操作只能cut在同一棵LCT上否则没有意思
add操作也只能在同一棵LCT上进行
查询也是如此
其实,修改查询的操作都可以原生态地使用LCT的辅助树:Splay来完成
而对于树的动态操作,一定要借助于LCT的函数来完成
最后给出完整的代码:
#include<cstdio>
#include<algorithm>
using namespace std;
const int INF=;
const int maxn=;
int read()
{
int x=;char ch=getchar();
while(ch<''||ch>'') ch=getchar();
while(ch>=''&&ch<='') {x=x*+ch-'';ch=getchar();}
return x;
}
int n,m,cnt,top;
bool rev[maxn];
int mx[maxn],fa[maxn],v[maxn],tag[maxn],last[maxn],q[maxn];
int c[maxn][];
struct edge{int to,next;}e[maxn<<];
void insert(int u,int v)
{
e[++cnt].to=v;e[cnt].next=last[u];last[u]=cnt;
e[++cnt].to=u;e[cnt].next=last[v];last[v]=cnt;
}
void update(int x)
{
int l=c[x][],r=c[x][];
mx[x]=max(mx[l],mx[r]);
mx[x]=max(mx[x],v[x]);
}
void pushdown(int x)
{
int l=c[x][],r=c[x][];
if(rev[x])
{
rev[l]^=;rev[r]^=;rev[x]^=;
swap(c[x][],c[x][]);
}
if(tag[x])
{
if(l){tag[l]+=tag[x];mx[l]+=tag[x];v[l]+=tag[x];}
if(r){tag[r]+=tag[x];mx[r]+=tag[x];v[r]+=tag[x];}
tag[x]=;
}
}
bool isroot(int x)
{
return c[fa[x]][]!=x&&c[fa[x]][]!=x;
}
void rotate(int x)
{
int y=fa[x],z=fa[y],l,r;
if(c[y][]==x)l=;else l=;r=l^;
if(!isroot(y))
{
if(c[z][]==y)c[z][]=x;
else c[z][]=x;
}
fa[x]=z;fa[y]=x;fa[c[x][r]]=y;
c[y][l]=c[x][r];c[x][r]=y;
update(y);update(x);
}
void splay(int x)
{
top=;q[++top]=x;
for(int i=x;!isroot(i);i=fa[i])
q[++top]=fa[i];
while(top)pushdown(q[top--]);
while(!isroot(x))
{
int y=fa[x],z=fa[y];
if(!isroot(y))
{
if(c[y][]==x^c[z][]==y)rotate(x);
else rotate(y);
}
rotate(x);
}
}
void access(int x)
{
for(int t=;x;t=x,x=fa[x])
splay(x),c[x][]=t,update(x);
}
void makeroot(int x)
{
access(x);splay(x);rev[x]^=;
}
void link(int x,int y)
{
makeroot(x);fa[x]=y;
}
void cut(int x,int y)
{
makeroot(x);access(y);splay(y);
c[y][]=fa[c[y][]]=;update(y);
}
int find(int x)
{
access(x);splay(x);
while(c[x][]) x=c[x][];
return x;
}
void add(int x,int y,int val)
{
makeroot(x);access(y);splay(y);
tag[y]+=val;mx[y]+=val;v[y]+=val;
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
for(int i=;i<=n;i++)
last[i]=tag[i]=rev[i]=fa[i]=c[i][]=c[i][]=;
mx[]=-INF;cnt=;
for(int i=;i<n;i++)
{
int u=read(),v=read();
insert(u,v);
}
for(int i=;i<=n;i++) mx[i]=v[i]=read();
q[++top]=;
for(int k=;k<=top;k++)
{
int now=q[k];
for(int i=last[now];i;i=e[i].next)
{
if(e[i].to!=fa[now])
{
fa[e[i].to]=now;
q[++top]=e[i].to;
}
}
}
m=read();
while(m--)
{
int opt=read(),x=read(),y=read(),w;
switch(opt)
{
case :
if(find(x)==find(y)) {puts("-1");break;}
link(x,y);break;
case :
if(find(x)!=find(y)||x==y) {puts("-1");break;}
cut(x,y);break;
case :
w=x;x=y;y=read();
if(find(x)!=find(y)) {puts("-1");break;}
add(x,y,w);break;
case :
if(find(x)!=find(y)){puts("-1");break;}
makeroot(x);access(y);splay(y);printf("%d\n",mx[y]);break;
}
}
puts("");
}
return ;
}
#include<cstdio>
#include<algorithm>
using namespace std;
const int INF=;
const int maxn=;
int read()
{
int x=;char ch=getchar();
while(ch<''||ch>'') ch=getchar();
while(ch>=''&&ch<='') {x=x*+ch-'';ch=getchar();}
return x;
}
int n,m,cnt,top;
bool rev[maxn];
int mx[maxn],fa[maxn],v[maxn],tag[maxn],last[maxn],q[maxn];
int c[maxn][];
struct edge{int to,next;}e[maxn<<];
void insert(int u,int v)
{
e[++cnt].to=v;e[cnt].next=last[u];last[u]=cnt;
e[++cnt].to=u;e[cnt].next=last[v];last[v]=cnt;
}
void update(int x)
{
int l=c[x][],r=c[x][];
mx[x]=max(mx[l],mx[r]);
mx[x]=max(mx[x],v[x]);
}
void pushdown(int x)
{
int l=c[x][],r=c[x][];
if(rev[x])
{
rev[l]^=;rev[r]^=;rev[x]^=;
swap(c[x][],c[x][]);
}
if(tag[x])
{
if(l){tag[l]+=tag[x];mx[l]+=tag[x];v[l]+=tag[x];}
if(r){tag[r]+=tag[x];mx[r]+=tag[x];v[r]+=tag[x];}
tag[x]=;
}
}
bool isroot(int x)
{
return c[fa[x]][]!=x&&c[fa[x]][]!=x;
}
void rotate(int x)
{
int y=fa[x],z=fa[y],l,r;
if(c[y][]==x)l=;else l=;r=l^;
if(!isroot(y))
{
if(c[z][]==y)c[z][]=x;
else c[z][]=x;
}
fa[x]=z;fa[y]=x;fa[c[x][r]]=y;
c[y][l]=c[x][r];c[x][r]=y;
update(y);update(x);
}
void splay(int x)
{
top=;q[++top]=x;
for(int i=x;!isroot(i);i=fa[i])
q[++top]=fa[i];
while(top)pushdown(q[top--]);
while(!isroot(x))
{
int y=fa[x],z=fa[y];
if(!isroot(y))
{
if(c[y][]==x^c[z][]==y)rotate(x);
else rotate(y);
}
rotate(x);
}
}
void access(int x)
{
for(int t=;x;t=x,x=fa[x])
splay(x),c[x][]=t,update(x);
}
void makeroot(int x)
{
access(x);splay(x);rev[x]^=;
}
void link(int x,int y)
{
makeroot(x);fa[x]=y;
}
void cut(int x,int y)
{
makeroot(x);access(y);splay(y);
c[y][]=fa[c[y][]]=;update(y);
}
int find(int x)
{
access(x);splay(x);
while(c[x][]) x=c[x][];
return x;
}
void add(int x,int y,int val)
{
makeroot(x);access(y);splay(y);
tag[y]+=val;mx[y]+=val;v[y]+=val;
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
for(int i=;i<=n;i++)
last[i]=tag[i]=rev[i]=fa[i]=c[i][]=c[i][]=;
mx[]=-INF;cnt=;
for(int i=;i<n;i++)
{
int u=read(),v=read();
insert(u,v);
}
for(int i=;i<=n;i++) mx[i]=v[i]=read();
q[++top]=;
for(int k=;k<=top;k++)
{
int now=q[k];
for(int i=last[now];i;i=e[i].next)
{
if(e[i].to!=fa[now])
{
fa[e[i].to]=now;
q[++top]=e[i].to;
}
}
}
m=read();
while(m--)
{
int opt=read(),x=read(),y=read(),w;
switch(opt)
{
case :
if(find(x)==find(y)) {puts("-1");break;}
link(x,y);break;
case :
if(find(x)!=find(y)||x==y) {puts("-1");break;}
cut(x,y);break;
case :
w=x;x=y;y=read();
if(find(x)!=find(y)) {puts("-1");break;}
add(x,y,w);break;
case :
if(find(x)!=find(y)){puts("-1");break;}
makeroot(x);access(y);splay(y);printf("%d\n",mx[y]);break;
}
}
puts("");
}
return ;
}
#include<cstdio>
#include<algorithm>
using namespace std;
const int INF=;
const int maxn=;
int read()
{
int x=;char ch=getchar();
while(ch<''||ch>'') ch=getchar();
while(ch>=''&&ch<='') {x=x*+ch-'';ch=getchar();}
return x;
}
int n,m,cnt,top;
bool rev[maxn];
int mx[maxn],fa[maxn],v[maxn],tag[maxn],last[maxn],q[maxn];
int c[maxn][];
struct edge{int to,next;}e[maxn<<];
void insert(int u,int v)
{
e[++cnt].to=v;e[cnt].next=last[u];last[u]=cnt;
e[++cnt].to=u;e[cnt].next=last[v];last[v]=cnt;
}
void update(int x)
{
int l=c[x][],r=c[x][];
mx[x]=max(mx[l],mx[r]);
mx[x]=max(mx[x],v[x]);
}
void pushdown(int x)
{
int l=c[x][],r=c[x][];
if(rev[x])
{
rev[l]^=;rev[r]^=;rev[x]^=;
swap(c[x][],c[x][]);
}
if(tag[x])
{
if(l){tag[l]+=tag[x];mx[l]+=tag[x];v[l]+=tag[x];}
if(r){tag[r]+=tag[x];mx[r]+=tag[x];v[r]+=tag[x];}
tag[x]=;
}
}
bool isroot(int x)
{
return c[fa[x]][]!=x&&c[fa[x]][]!=x;
}
void rotate(int x)
{
int y=fa[x],z=fa[y],l,r;
if(c[y][]==x)l=;else l=;r=l^;
if(!isroot(y))
{
if(c[z][]==y)c[z][]=x;
else c[z][]=x;
}
fa[x]=z;fa[y]=x;fa[c[x][r]]=y;
c[y][l]=c[x][r];c[x][r]=y;
update(y);update(x);
}
void splay(int x)
{
top=;q[++top]=x;
for(int i=x;!isroot(i);i=fa[i])
q[++top]=fa[i];
while(top)pushdown(q[top--]);
while(!isroot(x))
{
int y=fa[x],z=fa[y];
if(!isroot(y))
{
if(c[y][]==x^c[z][]==y)rotate(x);
else rotate(y);
}
rotate(x);
}
}
void access(int x)
{
for(int t=;x;t=x,x=fa[x])
splay(x),c[x][]=t,update(x);
}
void makeroot(int x)
{
access(x);splay(x);rev[x]^=;
}
void link(int x,int y)
{
makeroot(x);fa[x]=y;
}
void cut(int x,int y)
{
makeroot(x);access(y);splay(y);
c[y][]=fa[c[y][]]=;update(y);
}
int find(int x)
{
access(x);splay(x);
while(c[x][]) x=c[x][];
return x;
}
void add(int x,int y,int val)
{
makeroot(x);access(y);splay(y);
tag[y]+=val;mx[y]+=val;v[y]+=val;
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
for(int i=;i<=n;i++)
last[i]=tag[i]=rev[i]=fa[i]=c[i][]=c[i][]=;
mx[]=-INF;cnt=;
for(int i=;i<n;i++)
{
int u=read(),v=read();
insert(u,v);
}
for(int i=;i<=n;i++) mx[i]=v[i]=read();
q[++top]=;
for(int k=;k<=top;k++)
{
int now=q[k];
for(int i=last[now];i;i=e[i].next)
{
if(e[i].to!=fa[now])
{
fa[e[i].to]=now;
q[++top]=e[i].to;
}
}
}
m=read();
while(m--)
{
int opt=read(),x=read(),y=read(),w;
switch(opt)
{
case :
if(find(x)==find(y)) {puts("-1");break;}
link(x,y);break;
case :
if(find(x)!=find(y)||x==y) {puts("-1");break;}
cut(x,y);break;
case :
w=x;x=y;y=read();
if(find(x)!=find(y)) {puts("-1");break;}
add(x,y,w);break;
case :
if(find(x)!=find(y)){puts("-1");break;}
makeroot(x);access(y);splay(y);printf("%d\n",mx[y]);break;
}
}
puts("");
}
return ;
}
数据结构&图论:LCT的更多相关文章
- 数据结构&图论:欧拉游览树
ETT可以称为欧拉游览树,它是一种和欧拉序有关的动态树(LCT是解决动态树问题的一种方案,这是另一种) dfs序和欧拉序是把树问题转化到区间问题上然后再用数据结构去维护的利器 通过借助这两种形式能够完 ...
- 数据结构&图论:K短路-可持久化可并堆
本来A*就可以搞定的题,为了怕以后卡复杂度,找了个这么个方法 现阶段水平不够就不补充算法分析部分了 对于图G,建立一个以终点t为起点的最短路径构成的最短路径树 (就是反着跑一遍最短路,然后对于一个不为 ...
- 数据结构&图论:图
在这里对图的存储和遍历进行一个规范,为以后更复杂的数据结构学习打下基础 首先是邻接矩阵的形式,适合于存稠密图,如果是全连接图就再合适不过了 int a[maxn][maxn]; 一个二维数组就可以搞定 ...
- 数据结构(LCT动态树):BZOJ 1036: [ZJOI2008]树的统计Count
1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 12266 Solved: 4945[Submit ...
- 动态树之LCT(link-cut tree)讲解
动态树是一类要求维护森林的连通性的题的总称,这类问题要求维护某个点到根的某些数据,支持树的切分,合并,以及对子树的某些操作.其中解决这一问题的某些简化版(不包括对子树的操作)的基础数据结构就是LCT( ...
- SPLAY,LCT学习笔记(四)
前三篇好像变成了SPLAY专题... 这一篇正式开始LCT! 其实LCT就是基于SPLAY的伸展操作维护树(森林)连通性的一个数据结构 核心操作有很多,我们以一道题为例: 例:bzoj 2049 洞穴 ...
- LCT解读(1)
蒟蒻的LCT解读(1) 前段时间本蒟蒻自学了一下LCT,但是网上的很多资料并不很全,而且作为一个数组选手,我看指针代码真的很麻烦,所以就在这里写一篇数组选手能看懂的代码. LCT的初步了解 LCT全称 ...
- 动态树Link-cut tree(LCT)总结
动态树是个好玩的东西 LCT题集 预备知识 Splay 树链剖分(好像关系并不大) 动态树(Link-cut tree) 先搬dalao博客 什么是LCT? 动态树是一类要求维护森林的连通性的题的总称 ...
- LCT入门
前言 \(LCT\),真的是一个无比神奇的数据结构. 它可以动态维护链信息.连通性.边权.子树信息等各种神奇的东西. 而且,它其实并不难理解. 就算理解不了,它简短的代码也很好背. \(LCT\)与实 ...
随机推荐
- 冲刺ing-1
冲刺一 1.第一天的工作分配: 姓名 任务分工 吴伟华(队长) 布置团队任务,发表汇总博客及第一次冲刺博客 蔺皓雯 讨论任务分配 杨池宇 讨论任务分配 鲁婧楠 讨论任务分配 曾茜 讨论任务分配 蔡晨旸 ...
- 计算器软件实现系列(五)策略模式+asp.net
一 策略模式代码的编写 using System; using System.Collections.Generic; using System.Linq; using System.Web; /// ...
- 团队作业7——第二次项目冲刺(Beta版本)
团队作业7——第二次项目冲刺-Beta版本项目计划 团队作业7——第二次项目冲刺(Beta版本)-第一篇 团队作业7——第二次项目冲刺(Beta版本)-第二篇 团队作业7——第二次项目冲刺(Beta版 ...
- TCP系列34—窗口管理&流控—8、缓存自动调整
一.概述 我们之前介绍过一种具有大的带宽时延乘积(band-delay product.BDP)的网络,这种网络称为长肥网络(LongFatNetwork,即LFN).我们想象一种简单的场景,假设发送 ...
- foreach循环2
<select id="test" parameterType="java.util.List" resultType="user"& ...
- [剑指Offer] 64.滑动窗口的最大值
题目描述 给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值.例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6 ...
- 分布式消息队列RocketMQ&Kafka -- 消息的“顺序消费”
在说到消息中间件的时候,我们通常都会谈到一个特性:消息的顺序消费问题.这个问题看起来很简单:Producer发送消息1, 2, 3... Consumer按1, 2, 3...顺序消费. 但实际情况却 ...
- 网页中NPIO对Excel的操作实例
上一节是在wpf中实现对excel的操作方法,这一节看看网页中如何封装实现对excel的上传导入和下载保存的. 看看效果图:
- HttpServletRequestWrapper 是HttpServletRequest的包装类 ·关系相当于 int 与integer的关系
HttpServletRequestWrapper 是HttpServletRequest的包装类 ·关系相当于 int 与integer的关系
- Android UI设计的基本元素有哪些
在android app开发如火如荼的今天,如何让自己的App受人欢迎.如何增加app的下载量和使用量....成为很多android应用开发前,必须讨论的问题.而ui设计则是提升客户视觉体验度.提升下 ...