数据结构&图论: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\)与实 ...
随机推荐
- C中的除法,商和余数的大小、符号如何确定
对于C中的除法,商和余数的大小.符号是如何确定的呢?在C89中,只规定了如果两个数为正整数,那么余数的符号为正,并且商的值是接近真实值的最大整数.比如5 / 2,那么商就是2,余数就是1.但是,C89 ...
- 《剑指Offer》题十一~题二十
十一.旋转数组的最小数字 题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转.输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素.例如,数组{3, 4, 5, 1, 2}为{ ...
- Java微笔记(2)
Java 中方法的重载 一,如果同一个类中包含了两个或两个以上方法名相同.方法参数的个数.顺序或类型不同的方法,则称为方法的重载,也可称该方法被重载了 二,.判断方法重载的依据: 1. 必须是在同一个 ...
- Linux的ll命令详解
ll 列出来的结果详细,有时间,是否可读写等信息 ,象windows里的 详细信息 ls 只列出文件名或目录名 就象windows里的 列表 ll -t 是降序, ll -t | tac 是升序 l ...
- <Effective C++>读书摘要--Designs and Declarations<二>
<Item 20> Prefer pass-by-reference-to-const to pass-by-value 1.By default, C++ passes objects ...
- css深入理解之 border
一 border-width不支持百分比值 1 不符合客观逻辑 2 w3成都一种约定吧 3 边框本身就像是一个包裹内容的界限 类似的还有outline,box-shadow text-shadow均不 ...
- sublime Text3 设置用新标签页打开新的文件
今天用sublime Text3 打开项目文件,发现单击文件就可以打开,但是有一个问题:每次打开新文件就会覆盖当前的标签页,无法在新的标签页打开.于是在网上查了一下. 网上有人说在Preference ...
- == 和equal的区别?-005
1,== 和equal的区别? ==比较两个值是否相等,equal比较对对象的引用是否一致 举例: int a = 2; int b = 2; System.err.println(a == b);/ ...
- ORA-00933 SQL命令未正确结束 INSERT INTO ... SELECT
最近在修改数据库存储过程时,出现了一个ORA-00933错误, 执行的是 INSERT INTO...SELECT 语句,具体语句如下: INSERT INTO BASP_DX.QLR@GT(BDCD ...
- html/css/javascript知识点集锦;完全小白开搞web编程
知识点集锦 1.在html模板的代码中会有下面一类: {% block title %} Blog entries {% endblock %}这里的 block 和 endblock 是神马? {% ...