数据结构&图论: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\)与实 ...
随机推荐
- Sail
DescriptionThe polar bears are going fishing. They plan to sail from (sx,?sy) to (ex,?ey). However, ...
- ejabberd学习1
ejabberd是XMPP协议的一个实现,对大家的另一个意义就是,可以通过ejabberd来学习erlang. 1.从源码安装ejabberd. 可以参考ejabberd 简明配置.这个博文包括了安装 ...
- error LNK2019: 无法解析的外部符号 该符号在函数 中被引用 解决方案
需要添加lib或者dll库.项目-属性-配置属性-链接器-输入-附件依赖项,添加需要的lib. 例如我在运行OSG程序的时候,忘记添加了附件依赖项就会报这个错. 解决方案如图.
- 3dContactPointAnnotationTool开发日志(十七)
今天又改进了一下算法,把生成出来的接触点按中心坐标拍了个序,再把中心坐标一样的坐标点合并,x,y,z每个维度都只保留大的值.然后来看看效果: 先是1倍的,只剩下4096个接触点了,2^12个, ...
- Flink中的数据传输与背压
一图道尽心酸: 大的原理,上游的task产生数据后,会写在本地的缓存中,然后通知JM自己的数据已经好了,JM通知下游的Task去拉取数据,下游的Task然后去上游的Task拉取数据,形成链条. 但是在 ...
- Prepare方法和UnPrepare方法
Query组件提供的Prepare方法的作用是通知BDE或数据库服务器优化并准备执行SQL操作.Query的Prepare方法能优化执行的原因在于该方法是是在SQL语句执行前就对其进行分析.检查和编译 ...
- symbol lookup error *** , undefined symbol 错误
在重装samba过程后遇到一些问题,使用 gdb 时产生报错: gdb: symbol lookup error: gdb: undefined symbol: PyUnicodeUCS2_FromE ...
- Android Service 生命周期
Service概念及用途: Android中的服务,它与Activity不同,它是不能与用户交互的,不能自己启动的,运行在后台的程序,如果我们退出应用时,Service进程并没有结束,它仍然在后台运行 ...
- java 实现多个接口 方法重名的解决办法——内部类
package com.kk.innerClass; /** * 通过内部类实现接口 * 解决多个接口中方法重名问题 * */interface Machine { void run();} clas ...
- SDWebImage的使用说明
1. 在需要的地方导入头文件 #import "UIImageView+WebCache.h" webCache:网络缓存,几乎目前所有的浏览器都有一个内置的缓存,它们通常利用客户 ...