[ZJOI2016]大森林

XXI.[ZJOI2016]大森林

论LCT的百种用法系列

这题有几个性质:

1.询问与时间无关。因为只是添加新点,原来点之间的位置关系不变。因此只要询问的两个点都被建出来了,何时进行询问并无影响。

2.操作重叠的部分很多。因为我们不管怎么加点,即使是加原树中没有的点,仍然有原来点之间的位置关系不变。因此,我们每次加点操作都可以默认是所有树全都加点,不需要管原本\(l,r\)的限制。这样的话,某些相似的部分可能在很多树上都出现了。我们是否可以把相似的部分提取出来呢?

我们考虑差分对于生长节点的修改

对于每次修改,我们都新建一个虚点。在下一次修改之前,所有的生长效果都是在这个虚点上再挂上一个点。这样的话,就在更改生长节点时,就可以直接虚点一断一连即可。

生长操作就是这段代码:

if(t1==0)insnewreal(t2,t3),link(cntofall,lastvir);//a new real node, linked to the previous virtual

对于修改操作:

首先,关注到原题中的一段话:

\(1\ l\ r\ x\) 表示将第 \(l\) 棵树到第 \(r\) 棵树的生长节点改到标号为 \(x\) 的节点。对于 \(i\) \((l\leq i\leq r)\) 这棵树,如果标号 \(x\) 的点不在其中,那么这个操作对该树不产生影响

因为我们的生长都是所有树全都长,那就意味着有些原本没有\(x\)点的树也长出了\(x\)点。

因此,我们这个修改区间应该与\(x\)号节点的生长区间取交集。如果交集为空,直接跳过此次修改。

修改操作步骤如下:

  1. 取并集

  2. 新建代表此次修改的虚点,并将其父亲设为上次修改的虚点(即\(link\)一下)。

  3. 在树\(l\)处压入操作“将这次修改的虚点连到这次修改的目标生长节点上”(这应用了扫描线的思想)。

  4. 在树\(r+1\)处压入操作“将这次修改的虚点重新连回上次修改的虚点”。

  5. 将“上次修改的虚点”这一统计变量赋成本次修改的虚点。

对应代码:

if(t1==1){
	scanf("%d",&t4);
	t2=max(t2,nl[t4]);
	t3=min(t3,nr[t4]);
	if(t2>t3)continue;
	link(++cntofall,lastvir);//add a new virtual node
	v[t2].push_back(op(-1,cntofall,realid[t4]));//initially,the new virtual is linked to lastvir;after TREE t2, it is now linked to realid[t4];
	v[t3+1].push_back(op(-1,cntofall,lastvir));//and after t3+1, the new virtual is linked to lastvir again.
	lastvir=cntofall;
}

然后就是按照从\(1\)\(n\)的顺序遍历每棵树,依次进行修改和询问了。

哦,对,询问,怎么办呢?

首先这题我们最好不要写无根LCT,即使用\(makeroot\)的LCT。因为操作有点多,这个无根LCT的\(makeroot\)常数极大,按照我这种不写快读的写法是妥妥T的。那么我们应该如何实现查询路径长度呢?

首先一个非常合情合理的想法就是树上差分。假定我们要查询的是点对\((x,y)\),我们就找到\(LCA_{x,y}\),并尝试用一些值拼出路径长度。

假设我们已经找出\(LCA_{x,y}\),则我们只需要在每个节点维护splay中实子树大小,然后\(access(x),access(y),access(LCA)\),分别求出\(x\)\(ROOT\)\(y\)\(ROOT\)\(LCA\)\(ROOT\)的路径长度(当然,只包括实点,虚点不算\(size\))。答案即为\(s_x+s_y-2s_{LCA}\)

代码:

#include<bits/stdc++.h>
using namespace std;
#define lson t[x].ch[0]
#define rson t[x].ch[1]
int n,m,cntofreal,cntofall,cntofquery,lastvir,realid[200100],nl[200100],nr[200100],res[200100];
struct LCT{
	int fa,ch[2],sz,real;
}t[1001000];
inline int identify(int x){
	if(x==t[t[x].fa].ch[0])return 0;
	if(x==t[t[x].fa].ch[1])return 1;
	return -1;
}
inline void pushup(int x){t[x].sz=t[lson].sz+t[rson].sz+t[x].real;}
inline void rotate(int x){
	register int y=t[x].fa,z=t[y].fa,dirx=identify(x),diry=identify(y),b=t[x].ch[!dirx];
	if(diry!=-1)t[z].ch[diry]=x;t[x].fa=z;
	if(b)t[b].fa=y;t[y].ch[dirx]=b;
	t[y].fa=x,t[x].ch[!dirx]=y;
	pushup(y),pushup(x);
}
inline void splay(int x){for(;identify(x)!=-1;rotate(x))if(identify(t[x].fa)!=-1)rotate(identify(x)==identify(t[x].fa)?t[x].fa:x);}
inline int access(int x){register int y=0;for(;x;x=t[y=x].fa)splay(x),rson=y,pushup(x);return y;}
inline int findroot(int x){access(x),splay(x);while(lson)x=lson;splay(x);return x;}
inline void link(int x,int y){access(x),splay(x),t[x].fa=y;}
inline void cut(int x){access(x),splay(x),lson=t[lson].fa=0,pushup(x);}
inline int getdis(int x,int y){
	int sx,sy,sl,lca;
	access(x),splay(x),sx=t[x].sz;
	lca=access(y),splay(y),sy=t[y].sz;
	access(lca),splay(lca),sl=t[lca].sz;
	return sx+sy-2*sl;
}
void insnewreal(int l,int r){//insert a new node for all trees in section [l,r]
	t[realid[++cntofreal]=++cntofall].real=true;//cnt of all,the ID of all nodes in the LCT; real id,the ID of all the real nodes in the LCT
	nl[cntofreal]=l,nr[cntofreal]=r;//cnt of real, the ID of added real nodes
}
struct op{
	int id,x,y;
	op(int a=0,int b=0,int c=0){id=a,x=b,y=c;}
};
vector<op>v[100100];
int main(){
	scanf("%d%d",&n,&m);
	insnewreal(1,n);//initially, one real node for all
	link(++cntofall,1);//a initial virtual node, linking to the initial real node
	lastvir=cntofall;//last virtual, the previous virtual node's ID.
	for(int i=1,t1,t2,t3,t4;i<=m;i++){
		scanf("%d%d%d",&t1,&t2,&t3);
		if(t1==0)insnewreal(t2,t3),link(cntofall,lastvir);//a new real node, linked to the previous virtual
		if(t1==1){
			scanf("%d",&t4);
			t2=max(t2,nl[t4]);
			t3=min(t3,nr[t4]);
			if(t2>t3)continue;
			link(++cntofall,lastvir);//add a new virtual node
			v[t2].push_back(op(-1,cntofall,realid[t4]));//initially,the new virtual is linked to lastvir;after TREE t2, it is now linked to realid[t4];
			v[t3+1].push_back(op(-1,cntofall,lastvir));//and after t3+1, the new virtual is linked to lastvir again.
			lastvir=cntofall;
		}
		if(t1==2)scanf("%d",&t4),v[t2].push_back(op(++cntofquery,realid[t3],realid[t4]));
	}
	for(int i=1;i<=n;i++)for(int j=0;j<v[i].size();j++){
		if(v[i][j].id!=-1)res[v[i][j].id]=getdis(v[i][j].x,v[i][j].y);
		else cut(v[i][j].x),link(v[i][j].x,v[i][j].y);
	}
	for(int i=1;i<=cntofquery;i++)printf("%d\n",res[i]);
	return 0;
}

posted @ 2021-03-31 16:30  Troverld  阅读(54)  评论(0编辑  收藏  举报