一种动态树,可以处理动态问题的算法。
这是我个人的观点~~
有大神也是这么说的,像其他算法比如树链剖分,只能处理静态的数据或者在轻重链上的边或点的权值,对于其他动态的处理就毫无办法了。
而用裸的暴力绝对是要T的。
怎么办,引入LCT~~~~
LCT用来维护动态的森林,以及一些链上操作。
说实话初次接触LCT的时候,由于是0基础,然后左看右看不懂网上的题解,然后终于找到了论文,看了几遍才勉强懂这个概念~~(蒟蒻- -)
推荐论文:http://wenku.baidu.com/view/75906f160b4e767f5acfcedb
LCT是处理节点无序的有根树组成的森林,进行一些列操作(例如合并,剪切,翻转,更新·····)
对于一棵树的操作,我们可以用splay维护;同理,对于一片森林,也可以用多棵splay维护,而LCT就是要把这些森林的splay联系在一起。
而LCT的核心是access操作~
参照网上的意见,以这道题入手:
SPOJ - QTREE
Query on a tree
Description You are given a tree (an acyclic undirected connected graph) with N nodes, and edges numbered 1, 2, 3...N-1. We will ask you to perfrom some instructions of the following form:
InputThe first line of input contains an integer t, the number of test cases (t <= 20). t test cases follow. For each test case:
There is one blank line between successive tests. OutputFor each "QUERY" operation, write one integer representing its result. ExampleInput: 1 3 1 2 1 2 3 2 QUERY 1 2 CHANGE 1 3 QUERY 1 2 DONE Output: 1 3 Hint
|
咦,这道题不是树链剖分的入门题么? 对的,不过,也可以用LCT做(树链剖分只处理静态的树和有关轻重边的动态更新,而LCT就是用来解决动态树问题)
题意:
给定一棵树,给定每条边a,b,w (w权值)
完成两个操作:
1.把第i条边权值改成w
2.查询u到v路径上的最大权值
介绍一下access操作:查询根到u这条路径,并把这条路径更新为偏爱路径(之前的偏爱边可能会有改变)被访问到的u,不再有偏爱儿子。
每次access访问,都会把这条路径上的点用一棵splay维护,splay维护的关键词是点的深度,保证左边的子树比当前点深度小,右边的子树比当前点深度大。
并且这棵树把u作为根节点之后不再有右儿子(u没有偏爱儿子).
而splay要维护的是一条偏爱路径上的点。所有的splay会构成一个森林的集合。
要注意的是,在rotate判断ff的时候,有可能ff的左右儿子都不是f,然而没有进行判断就会跑出神奇的错误,我第一次写就挂在了这里,调了好久。
剩下的代码需要积累出技巧,慢慢入门- -
#include
#include
#include
#include
#include
#include
#include
using namespace std;
/*
LCT的思想类似于轻重链剖分
是用偏爱路径划分,用splay维护一条偏爱路径,然后像森林操作一样搞
u-v边的权值记录在v点上
*/
#define maxn 10005
int n;
int fa[maxn];
struct edge
{
int v,w;
edge *next;
edge(int _v,int _w,edge *_next)
{
v=_v;
w=_w;
next=_next;
}
}*head[maxn];
struct node
{
node *f;
node *ch[2];
bool root;//是否是所在辅助树的根节点
int cost;
int maxcost;
}tree[maxn],*null,Tnull;
void init(node *u)
{
u->f=u->ch[0]=u->ch[1]=null;
u->root=true;
u->cost=u->maxcost=0;
}
void pushup(node *p)
{
p->maxcost=max(max(p->ch[0]->maxcost,p->ch[1]->maxcost),p->cost);
}
void rotate(node *u)
{
node *f=u->f;
node *ff=f->f;
int d=u==f->ch[1];
f->ch[d]=u->ch[d^1];
if(u->ch[d^1]!=null)u->ch[d^1]->f=f;
u->f=ff;
if(ff!=null)
{
if(ff->ch[0] == f)
{
ff->ch[0] = u;
}
else if(ff->ch[1] == f)//一定要用Else If,如果直接用Else会出现错误,因为树本身可能不是二叉树,虽然生成的Splay Tree是
{
ff->ch[1] = u;
}
}
u->ch[d^1]=f;
f->f=u;
pushup(f);
pushup(u);
if(f->root)
{
u->root=true;
f->root=false;
}
}
void splay(node *u)
{
if(u==null)return ;
while(!u->root)
{
node *f=u->f;
if(f->root)
{
rotate(u);
}
else
{
node *ff=f->f;
int d=u==f->ch[1];
int dd=f==ff->ch[1];
if(d==dd)rotate(f);
else rotate(u);
rotate(u);
}
}
pushup(u);
}
vector >E;
char ss[20];
void access(node *u,bool flag)
{
node *v=null;
while(u!=null)
{
splay(u);
if(flag)
{
if(u->f==null)
{
printf("%d\n",max(u->ch[1]->maxcost,v->maxcost));
}
}
u->ch[1]->root=true;
u->ch[1]=v;
v->root=false;
pushup(u);
v=u;
u=u->f;
}
}
void query(int u,int v)
{
access(tree+u,0);
access(tree+v,1);
}
void change(int u,int w)
{
access(tree+u,0);
splay(tree+u);
tree[u].cost=w;
pushup(tree+u);
}
void bfs()
{
memset(fa,-1,sizeof(fa));
queueq;
q.push(1);
fa[1]=0;
while(!q.empty())
{
int u=q.front();
q.pop();
for(edge *i=head[u];i;i=i->next)
{
if(fa[i->v]==-1)
{
fa[i->v]=u;
tree[i->v].f=tree+u;
tree[i->v].cost=tree[i->v].maxcost=i->w;
q.push(i->v);
}
}
}
}
int main()
{
int T;
scanf("%d",&T);
null=&Tnull;
init(null);
while(T--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
head[i]=NULL;
init(&tree[i]);
}
E.clear();
for(int i=1;i
然后,再来做操作多一点的:http://acm.hdu.edu.cn/showproblem.php?pid=4010
给定一棵树,维护4个操作:
1.给定u,v,如果不在一棵树上,它们之间连边。
2.给定u,v如果u!=v且在同一棵树上,把u设为整棵树的根,再把v和它的父亲断开。
3.给定u,v,w,如果u,v在同一条树上,把他们之间的点的权值+w
4.给定u,v,如果u,v在同一棵树上,求出他们路径上的最大值。
违法操作输出-1
连边操作,访问u,把u旋到根,把它左右翻转即可(翻转前splay上u一定没有右节点(没有偏爱儿子),翻转后一定没有左儿子,说明它就是从根到u路径上深度最小的了,就成为了根)
把它接到v上
设为整棵树的根:一样的先access(u),splay(u)再rev(u)。
断开:access(u),splay(u),但不翻转,这样u没有右儿子但有左儿子,和左子树断开即可。
对于3、4操作,可以先把u设为根,再access(v)然后找到u-v这条路径上splay的根节点在哪里,信息都记录在那个节点上!
我也是蒟蒻啊,才开始慢慢理解LCT的access.
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn=300000+20;
const int inf=0x3f3f3f3f;
struct node
{
node *f;
node *ch[2];
bool rev;
int add;
int mm;
int key;
}tree[maxn],*null,*cur;
void init()
{
null=tree;
null->f=null->ch[0]=null->ch[1]=null;
null->rev=0;
null->add=0;
null->mm=null->key=-inf;
cur=tree+1;
}
node *newnode(int key)
{
cur->f=cur->ch[0]=cur->ch[1]=null;
cur->mm=cur->key=key;
cur->add=0;
cur->rev=0;
return cur++;
}
bool isroot(node *x)
{
return x==null||x->f->ch[0]!=x&&x->f->ch[1]!=x;
}
void pushup(node *u)
{
u->mm=max(u->key,max(u->ch[0]->mm,u->ch[1]->mm));
}
void pushdown(node *u)
{
if(u==null)return ;
if(u->rev)
{
swap(u->ch[0],u->ch[1]);
if(u->ch[0]!=null)u->ch[0]->rev^=1;
if(u->ch[1]!=null)u->ch[1]->rev^=1;
u->rev=0;
}
if(u->add)
{
if(u->ch[0]!=null)
{
u->ch[0]->add+=u->add;
u->ch[0]->mm+=u->add;
u->ch[0]->key+=u->add;
}
if(u->ch[1]!=null)
{
u->ch[1]->add+=u->add;
u->ch[1]->mm+=u->add;
u->ch[1]->key+=u->add;
}
u->add=0;
}
}
void rotate(node *u)
{
node *f=u->f;
node *ff=f->f;
int d=u==f->ch[1];
if(u->ch[d^1]!=null)u->ch[d^1]->f=f;
f->ch[d]=u->ch[d^1];
u->f=ff;
if(ff!=null)
{
if(f==ff->ch[0])ff->ch[0]=u;
else if(f==ff->ch[1])ff->ch[1]=u;
}
u->ch[d^1]=f;
f->f=u;
pushup(f);
pushup(u);
}
node *sta[maxn];
int cnt;
void splay(node *u)
{
if(u==null)return ;
cnt=1;
sta[0]=u;
for(node *y=u;!isroot(y);y=y->f)
{
sta[cnt++]=y->f;
}
while(cnt)pushdown(sta[--cnt]);
while(!isroot(u))
{
node *f=u->f;
node *ff=f->f;
if(isroot(f))
{
rotate(u);
}
else
{
int d=u==f->ch[1];
int dd=f==ff->ch[1];
if(d==dd)rotate(f);
else rotate(u);
rotate(u);
}
}
pushup(u);
}
node *access(node *u)
{
node *v=null;
while(u!=null)
{
splay(u);
v->f=u;
u->ch[1]=v;
pushup(u);
v=u;
u=u->f;
}
return v;
}
void link(node *u,node *v)
{
access(u);
splay(u);
u->rev=1;
u->f=v;
}
bool sam(node *u,node *v)
{
while(u->f!=null)u=u->f;
while(v->f!=null)v=v->f;
return u==v;
}
void changeroot(node *u)
{
access(u)->rev^=1;
}
void cut(node *u)
{
access(u);
splay(u);//access+旋转之后左子树的点都比u小,一定会有u的父亲
u->ch[0]=u->ch[0]->f=null;
pushup(u);
}
node *getroot(node *u)
{
access(u);
splay(u);
while(u->f!=null)u=u->f;
splay(u);
return u;
}
int n,m;
int det[maxn];
struct edge
{
int u,v;
}E[maxn];
int main()
{
while(scanf("%d",&n)!=EOF)
{
init();
for(int i=1;iadd+=w;
q->mm+=w;
q->key+=w;
}
}
else if(k==4)
{
scanf("%d%d",&a,&b);
if(!sam(tree+a,tree+b))printf("-1\n");
else
{
changeroot(tree+a);
access(tree+b);
node *q=getroot(tree+b);
printf("%d\n",q->mm);
}
}
}
printf("\n");
}
return 0;
}