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的更多相关文章

  1. 数据结构&图论:欧拉游览树

    ETT可以称为欧拉游览树,它是一种和欧拉序有关的动态树(LCT是解决动态树问题的一种方案,这是另一种) dfs序和欧拉序是把树问题转化到区间问题上然后再用数据结构去维护的利器 通过借助这两种形式能够完 ...

  2. 数据结构&图论:K短路-可持久化可并堆

    本来A*就可以搞定的题,为了怕以后卡复杂度,找了个这么个方法 现阶段水平不够就不补充算法分析部分了 对于图G,建立一个以终点t为起点的最短路径构成的最短路径树 (就是反着跑一遍最短路,然后对于一个不为 ...

  3. 数据结构&图论:图

    在这里对图的存储和遍历进行一个规范,为以后更复杂的数据结构学习打下基础 首先是邻接矩阵的形式,适合于存稠密图,如果是全连接图就再合适不过了 int a[maxn][maxn]; 一个二维数组就可以搞定 ...

  4. 数据结构(LCT动态树):BZOJ 1036: [ZJOI2008]树的统计Count

    1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 12266  Solved: 4945[Submit ...

  5. 动态树之LCT(link-cut tree)讲解

    动态树是一类要求维护森林的连通性的题的总称,这类问题要求维护某个点到根的某些数据,支持树的切分,合并,以及对子树的某些操作.其中解决这一问题的某些简化版(不包括对子树的操作)的基础数据结构就是LCT( ...

  6. SPLAY,LCT学习笔记(四)

    前三篇好像变成了SPLAY专题... 这一篇正式开始LCT! 其实LCT就是基于SPLAY的伸展操作维护树(森林)连通性的一个数据结构 核心操作有很多,我们以一道题为例: 例:bzoj 2049 洞穴 ...

  7. LCT解读(1)

    蒟蒻的LCT解读(1) 前段时间本蒟蒻自学了一下LCT,但是网上的很多资料并不很全,而且作为一个数组选手,我看指针代码真的很麻烦,所以就在这里写一篇数组选手能看懂的代码. LCT的初步了解 LCT全称 ...

  8. 动态树Link-cut tree(LCT)总结

    动态树是个好玩的东西 LCT题集 预备知识 Splay 树链剖分(好像关系并不大) 动态树(Link-cut tree) 先搬dalao博客 什么是LCT? 动态树是一类要求维护森林的连通性的题的总称 ...

  9. LCT入门

    前言 \(LCT\),真的是一个无比神奇的数据结构. 它可以动态维护链信息.连通性.边权.子树信息等各种神奇的东西. 而且,它其实并不难理解. 就算理解不了,它简短的代码也很好背. \(LCT\)与实 ...

随机推荐

  1. C#中委托的发展与匿名函数

    匿名函数(C# 编程指南) 匿名函数是一个“内联”语句或表达式,可在需要委托类型的任何地方使用. 可以使用匿名函数来初始化命名委托,或传递命名委托(而不是命名委托类型)作为方法参数. 共有两种匿名函数 ...

  2. linux下php环境配置

    参: http://www.laozuo.org/5542.html LAMP,基于Linux/Apache/MySQL/PHP架构的网站建设环境,对于一般的网站来说足够使用,如果我们的网站访问量或者 ...

  3. 模拟Excel同一列相同值的单元格合并

    背景 项目中有一个查询工作量,可以将查询的结果导出到Excel表中.在Excel工具中,有一个合并居中功能,可以将选中的单元格合并成一个大的单元格.现在需要在程序中直接实现查询结果的汇总, 问题分析 ...

  4. lol人物模型提取(六)

      模型昨天就已经做出来了,不过到上色这一块貌似又遇到了一些问题.由于模型的眼睛比较小,没法做出亮光效果,上不了UV,只能做哑光效果的.   亮光效果:   哑光效果:   很显然亮光效果更加好看一点 ...

  5. 201621123033 《Java程序设计》第5周学习总结

    1. 本周学习总结 1.1 写出你认为本周学习中比较重要的知识点关键词 接口 Comparable Comparator 1.2 尝试使用思维导图将这些关键词组织起来.注:思维导图一般不需要出现过多的 ...

  6. win7系统日志分支删除方法

    这篇日志有问题,自己亲身尝试失败,这里只提供思路,谢谢(改天突破再做修改) 之前电脑装过德国的叫啥子软件来着,当时在系统自动创建了日志,后来软件卸载了,发现还是有这个日志主键,(我有强迫症)心里不爽, ...

  7. Windows Sever 2008隐藏和系统属性

    由于有些目录为隐藏和系统属性,首先要把 显示系统文件和显示所有文件 功能开启,把隐藏文件和目录显出来. 1.C:\Windows\Web\Wall*** 自带墙纸,不需要的可以删除掉. 2.C:\Wi ...

  8. 《Effective C#》快速笔记(六)- - C# 高效编程要点补充

    目录 四十五.尽量减少装箱拆箱 四十六.为应用程序创建专门的异常类 四十七.使用强异常安全保证 四十八.尽量使用安全的代码 四十九.实现与 CLS 兼容的程序集 五十.实现小尺寸.高内聚的程序集 这是 ...

  9. [剑指Offer] 49.把字符串转换成整数

    题目描述 将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数. 数值为0或者字符串不是一个合法的数值则返回0  [思路]考虑所有特殊情况 1.数字前面有空格,如s="    12 ...

  10. 【bzoj1093】[ZJOI2007]最大半连通子图 Tarjan+拓扑排序+dp

    题目描述 一个有向图G=(V,E)称为半连通的(Semi-Connected),如果满足:对于u,v∈V,满足u→v或v→u,即对于图中任意两点u,v,存在一条u到v的有向路径或者从v到u的有向路径. ...