• [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;
    }
    

  • 相关阅读:
    vue
    vue
    vue 中使用style(样式)
    vue 中使用class(样式)
    第17课
    第16课
    第15课
    第14课
    第13课
    第12课
  • 原文地址:https://www.cnblogs.com/Troverld/p/14602165.html
Copyright © 2020-2023  润新知