[补充]LCT有关细节的处理及理解

由于初学,对LCT各种操作还不是很会.


然后对于对边权操作和对点权操作还不熟悉, 所以大致分为两类。

第一类:修改更新边的权值:dfs建树:

void dfs(int u,int f)
{
	for(int i=head[u];~i;i=e[i].next)
	{
		int v=e[i].v;
		if(v==f)continue;
		fa[v]=u;
		tree[v].cost=tree[v].maxcost=e[i].w;
		tree[v].f=tree+u;
		dfs(v,u);
	}
}

第二类:修改点的权值:(先单独建立点的信息,再把点连起来)

node *newnode(int key)
{
        f=cur->ch[0]=cur->ch[1]=null;
        cur->mm=cur->key=key;
        cur->add=0;
        cur->rev=0;
        return cur++;
}
void link(node *u,node *v)
{
	access(u);
	splay(u);
	u->rev=1;
	u->f=v;
}

而后可以记录两种东西保存是否是当前splay树的根,root标记或者isroot.

细节处理root上注意:

node *access(node *u)
{
	node *v=null;
	while(u!=null)
	{
		splay(u);
		if(ok)
		{
			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;
	}
	return v;
}
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);
	if(f->root)//每次rotate需要更新这里
	{
		u->root=true;
		f->root=false;
	}
}

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

☆☆☆☆☆对于建边,考虑了很久到底采用哪种版,最后总结出了一个适合自己的版。


//初始化点    null=&Tnull;init(null);  
void init(node *u)
{
	u->f=u->ch[0]=u->ch[1]=null;
	u->rev=0;
	u->cost=u->maxcost=u->add=0;
} //对于每个点都进行初始化
for(int i=1;i<=n;i++)
{
        head[i]=-1;
        init(&tree[i]);
}//初始化

读完信息后dfs建树:

void dfs(int u,int f)
{
	tree[u].cost=tree[u].maxcost=det[u];
	if(f!=-1)tree[u].f=tree+f;
	else tree[u].f=null;
	for(int i=head[u];~i;i=e[i].next)
	{
		int v=e[i].v;
		if(v==f)continue;
		dfs(v,u);
	}
}

然后是对于root还是isroot()的选择。我觉得还是尽可能的选择isroot,函数也需要方便一点。

而且isroot不需要在更新的时候维护,而且可能要快一些:



 

上面的是isroot,下面的是root,不仅要快一些,空间也省了一些.

bool isroot(node *u)
{
	return u==null||u->f->ch[0]!=u&&u->f->ch[1]!=u;
}

rev操作:对于link或者changeroot等操作需要rev标记,直接pushdown好了。

pushdown:在splay的时候开一个栈,然后把它到根这条路径上每个点先pushdown(从上到下).,一样的pushdown,lazy标记  (rev的优先级高于其他lazy?我不造)

void pushdown(node *u)
{
	if(u==null)return ;
	if(u->rev)//翻转lazy
	{
		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)//增加lazy
	{
		if(u->ch[0]!=null)
		{
			u->ch[0]->cost+=u->add;
			u->ch[0]->maxcost+=u->add;
			u->ch[0]->add+=u->add;
		}
		if(u->ch[1]!=null)
		{
			u->ch[1]->cost+=u->add;
			u->ch[1]->maxcost+=u->add;
			u->ch[1]->add+=u->add;
		}
		u->add=0;
	}
}

rotate:细节----每次rotate之后只需要更新f的信息就好了,不然多次updata会很慢。

(原理:参照论文http://wenku.baidu.com/view/75906f160b4e767f5acfcedb  看它里面的rotate操作讲解)

其实简单点说就是rotate一定是在splay里面进行的,splay(u),u的信息只需要在最后更新就好了

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];
	
	if(ff!=null)
	{
		if(f==ff->ch[0])ff->ch[0]=u;
		else if(f==ff->ch[1])ff->ch[1]=u;
	}
	u->f=ff;
	
	f->f=u;
	u->ch[d^1]=f;
	
	pushup(f);
}

splay:(预处理pushdown)

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;
		if(isroot(f))
		{
			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);
}

同一颗子树判断(一直往根找):

bool sam(node *u,node *v)
{
	while(u->f!=null)u=u->f;
	while(v->f!=null)v=v->f;
	return u==v;
}

access(写成指针返回是利于changeroot)

node * access(node *u)
{
	node *v=null;
	while(u!=null)
	{
		splay(u);
		u->ch[1]=v;
		v->f=u;//和root写法不同
		pushup(u);
		v=u;
		u=u->f;
	}
	return v;
}


link:原理:access之后u是它所在splay上深度最大的点,一定在最右边(该splay记录u到整棵树root的路径),splay之后就没有右子树,翻转之后它就是深度最小的点,就是根了,然后把这棵树接到v上就完成了合并

void link(node *u,node *v)
{
	access(u);
	splay(u);
	u->rev=1;
	u->f=v;
}
changeroot:原理同上,少一个操作

void changeroot(node *u)
{
	access(u)->rev^=1;//access返回的是u所在splay的根(返回的是v,u==null所以v是根)
}


cut:原理差不多,不翻转而已(u为splay的根后,左边的点深度都比它小,断开这个链接就好了)

void cut(node *u)
{
	access(u);
	splay(u);
	u->ch[0]=u->ch[0]->f=null;
	pushup(u);
}


getroot(得到某个点所在splay上的root(changeroot只改变原来那棵树的根(核心是改变深度),getroot得到splay的根)
node *getroot(node *u)
{
	access(u);
	splay(u);
	while(u->f!=null)u=u->f;
	splay(u);
	return u;
}

对于u到v的操作,先把u设为根,再access(v),然后找到u-v上的splay的根,对它进行修改或读取信息。

void query(node *u,node *v)
{
	changeroot(u);
	splay(v);
	node *q=getroot(v);
	printf("%d\n",q->maxcost);
}




剩下的慢慢补充

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值