zoukankan      html  css  js  c++  java
  • P3348[ZJOI2016]大森林【LCT】

    正题

    题目链接:https://www.luogu.com.cn/problem/P3348


    题目大意

    \(n\)棵树开始只有一个编号为\(1\)的节点且为标记点。\(m\)次操作要求支持

    1. \(l\sim r\)的树中的标记点下面加入一个新的编号的节点
    2. \(l\sim r\)的树上的标记点改为\(x\)(如果没有节点\(x\)就不操作)
    3. 询问第\(x\)棵树上\(u\)点到\(v\)点的距离

    \(1\leq n\leq 10^5,1\leq m\leq 2\times 10^5\)
    保证询问合法


    解题思路

    保证询问合法的话我们其实第一个操作理解为对所有树都操作就可以了。主要是第二个操作,在线区间\(LCT\)看起来就很不可做,所以考虑离线。

    对于一个操作\(1\ l\ r\ x\)它会对\(l-1\)\(l\)的树造成的影响是再往后直到下一个\(1\)操作之间所有的节点都会被接到不同的点下面。但是显然暴力改接是不行的,我们可以考虑对于两个\(1\)操作之间的\(0\)操作建立一个虚点下面链接的所有这个区间新建的点,然后每次就改接一个虚点就好了。

    然后需要注意的一些细节:因为根是固定的不能用\(split\),会破坏父子关系(好像在\(makeroot(1)\)回去可以,但是据说很慢?),所以要差分求到根节点的路径长度。还要求\(lca\)\(LCT\)上求\(lca\)的话就\(access\)\(x\)再到\(y\)最后\(Splay\)的那个\(y\)就是\(lca\)了。

    还有因为如果没有节点\(x\)就不操作,所以我们需要记录一下每个点拥有的树的区间然后取一个交集就好了。

    时间复杂度\(O(n\log n)\)


    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int N=2e5+10;
    struct node{
    	int x,l,r,id;
    }q[N],c[N];
    int n,m,num,cnt,ct,qt;
    int L[N],R[N],ans[N],at[N];
    struct LCT{
    	int fa[N],t[N][2],siz[N],v[N];
    	bool Nroot(int x)
    	{return fa[x]&&(t[fa[x]][0]==x||t[fa[x]][1]==x);}
    	bool Direct(int x)
    	{return t[fa[x]][1]==x;}
    	void PushUp(int x){
    		siz[x]=siz[t[x][0]]+siz[t[x][1]]+v[x];
    		return;
    	}
    	void Rotate(int x){
    		int y=fa[x],z=fa[y];
    		int xs=Direct(x),ys=Direct(y);
    		int w=t[x][xs^1];
    		if(Nroot(y))t[z][ys]=x;
    		t[x][xs^1]=y;t[y][xs]=w;
    		if(w)fa[w]=y;fa[y]=x;fa[x]=z;
    		PushUp(y);PushUp(x);return;
    	}
    	void Splay(int x){
    		while(Nroot(x)){
    			int y=fa[x];
    			if(!Nroot(y))Rotate(x);
    			else if(Direct(x)==Direct(y))
    				Rotate(y),Rotate(x);
    			else Rotate(x),Rotate(x);
    		}
    		return;
    	}
    	int Access(int x){
    		int y=0,px=x;
    		for(;x;y=x,x=fa[x])
    			Splay(x),t[x][1]=y,PushUp(x);
     		Splay(px);return y;
    	}
    	void Link(int x,int y)
    	{Splay(x);fa[x]=y;return;}
    	void Cut(int x)
    	{Access(x);fa[t[x][0]]=0;t[x][0]=0;PushUp(x);return;}
    }T;
    bool cmp(node x,node y)
    {return x.x<y.x;}
    int main()
    {
    	scanf("%d%d",&n,&m);
    	L[1]=cnt=at[1]=1;R[1]=n;
    	T.Link(2,1);cnt=2;int last=2,num=1,aux=2;
    	for(int i=1;i<=m;i++){
    		int op,l,r,x;
    		scanf("%d%d%d",&op,&l,&r);
    		if(op==0){
    			++num;at[num]=++cnt;
    			T.v[cnt]=T.siz[cnt]=1;
    			T.Link(cnt,aux);
    			L[num]=l;R[num]=r;
    		}
    		else if(op==1){
    			scanf("%d",&x);
    			l=max(l,L[x]);r=min(r,R[x]);
    			if(l>r)continue;
    			++cnt;T.Link(cnt,aux);
    			c[++ct]=(node){l,cnt,at[x]};
    			c[++ct]=(node){r+1,cnt,aux,0};
    			aux=cnt;
    		}
    		else{
    			scanf("%d",&x);
    			q[++qt]=(node){l,at[r],at[x],qt};
    		}
    	}
    	sort(q+1,q+1+qt,cmp);
    	sort(c+1,c+1+ct,cmp);
    	for(int i=1,z=1;i<=qt;i++){
    		int sum=0;
    		while(z<=ct&&c[z].x<=q[i].x)
    			T.Cut(c[z].l),T.Link(c[z].l,c[z].r),z++;
    		T.Access(q[i].l);sum+=T.siz[q[i].l];
    		int lca=T.Access(q[i].r);sum+=T.siz[q[i].r];
    		T.Access(lca);sum-=2*T.siz[lca];
    		ans[q[i].id]=sum;
    	}
    	for(int i=1;i<=qt;i++)
    		printf("%d\n",ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    Ubuntu 16.04 compare 软件安装
    ubuntu 18 常用软件安装
    LSTM时间序列预测学习
    ubuntu 16.04 屏幕截图
    ubuntu 16.04 tensorboard 学习
    ubuntu 16 .04常见指令整理
    ABAP 更改销售订单(BAPI'BAPI_SALESORDER_CHANGE')
    ABAP SM30表维护生成器,新加一列描述仅供用户维护时参考,不存内表。(例如物料描述,客户描述)
    93年到底多少岁
    一个93年的中年人对2019年的总结
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/14481919.html
Copyright © 2011-2022 走看看