• UOJ#195. 【ZJOI2016】大♂森林 LCT


    原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ195.html

    题解

      首先询问都可以放到最后处理。

      对于操作,我们把它差分一下离线下来。

      现在的问题就是从第一棵树到第 n 棵树扫一遍,并不断维护树的形态。

      容易感受到这棵树会有删节点之类的操作,所以自然想到 LCT 。

      但是要涉及一个节点的一些子节点换父亲的时候LCT就GG了。

      解决这个问题的办法是建立虚点。虚点权值为 0 ,实点权值为 1,于是我们要维护链上点权和。

      建虚点的规则是:

      对于每一个操作 1 都建立一个虚点。即:将区间 [L,R] 的生长节点换成 x 的时候,建一个虚点,这个虚点的父亲 在 [L,R] 中是 x ,否则是上一个生长节点对应的虚点。

      对于每一个操作 2 ,直接把他连到上一个生长节点所对应的虚点上去就好了。

      我们发现我们维护的LCT要获取父亲信息,所以不方便换根。那就不换了!

      那么怎么求两点距离呢?

      dis(x,y) = depth[x]+depth[y] - 2*depth[LCA(x,y)] 

      LCT怎么求LCA 呢?见代码中的函数 Ask()

      时间复杂度 $O(nlog n)$ 。

    代码

    #include <bits/stdc++.h>
    #define clr(x) memset(x,0,sizeof (x))
    using namespace std;
    typedef long long LL;
    LL read(){
    	LL x=0,f=0;
    	char ch=getchar();
    	while (!isdigit(ch))
    		f|=ch=='-',ch=getchar();
    	while (isdigit(ch))
    		x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    	return f?-x:x;
    }
    const int N=200005*2;
    int n,m,oc=0;
    struct Opt{
    	int t,lev,x,y;
    	Opt(){}
    	Opt(int _t,int _lev,int _x,int _y){
    		t=_t,lev=_lev,x=_x,y=_y;
    	}
    }o[N*3];
    bool cmpo(Opt a,Opt b){
    	return a.t!=b.t?a.t<b.t:a.lev<b.lev;
    }
    int lv[N],rv[N],id[N],ans[N];
    namespace lct{
    	int size[N],val[N],fa[N],son[N][2];
    	int n;
    	void pushup(int x){
    		size[x]=size[son[x][0]]+val[x]+size[son[x][1]];
    	}
    	int Add(int v){
    		val[++n]=v,pushup(n);
    		return n;
    	}
    	void init(){
    		n=0;
    		id[1]=Add(1);
    	}
    	int isroot(int x){
    		return son[fa[x]][0]!=x&&son[fa[x]][1]!=x;
    	}
    	int wson(int x){
    		return son[fa[x]][1]==x;
    	}
    	void rotate(int x){
    		if (isroot(x))
    			return;
    		int y=fa[x],z=fa[y],L=wson(x),R=L^1;
    		if (!isroot(y))
    			son[z][wson(y)]=x;
    		fa[x]=z,fa[y]=x,fa[son[x][R]]=y;
    		son[y][L]=son[x][R],son[x][R]=y;
    		pushup(y),pushup(x);
    	}
    	void splay(int x){
    		for (int y=fa[x];!isroot(x);rotate(x),y=fa[x])
    			if (!isroot(y))
    				rotate(wson(x)==wson(y)?y:x);
    	}
    	int access(int x){
    		int t;
    		for (t=0;x;t=x,x=fa[x])
    			splay(x),son[x][1]=t,pushup(x);
    		return t;
    	}
    	void refather(int x,int y){
    		access(x),splay(x),son[x][0]=fa[son[x][0]]=0,pushup(x);
    		fa[x]=y;
    	}
    	int Ask(int x,int y){
    		int ans=0;
    		access(x),splay(x),ans=size[x];
    		int z=access(y);
    		splay(y),ans+=size[y];
    		access(z),splay(z),ans-=size[z]*2;
    		return ans;
    	}
    }
    int main(){
    	n=read(),m=read();
    	lct::init();
    	lv[1]=1,rv[1]=n;
    	lct::refather(lct::Add(0),1);
    	int pre=2,cnt=1,q=0;
    	for (int i=1;i<=m;i++){
    		int type=read();
    		if (type==0){
    			int L=read(),R=read();
    			cnt++,lv[cnt]=L,rv[cnt]=R;
    			o[++oc]=Opt(1,i,id[cnt]=lct::Add(1),pre);
    		}
    		else if (type==1){
    			int L=read(),R=read(),x=read();
    			L=max(L,lv[x]),R=min(R,rv[x]);
    			if (L<=R){
    				int now=lct::Add(0);
    				if (L>1)
    					lct::refather(now,pre);
    				o[++oc]=Opt(L,i,now,id[x]);
    				o[++oc]=Opt(R+1,i,now,pre);
    				pre=now;
    			}
    		}
    		else {
    			int k=read(),x=read(),y=read();
    			o[++oc]=Opt(k,(++q)+m,id[x],id[y]);
    		}
    	}
    	sort(o+1,o+oc+1,cmpo);
    	for (int i=1,j=1;i<=n;i++){
    		while (j<=oc&&o[j].t<=i){
    			int k=o[j].lev,x=o[j].x,y=o[j].y;
    			if (k<=m)
    				lct::refather(x,y);
    			else
    				ans[k-m]=lct::Ask(x,y);
    			j++;
    		}
    	}
    	for (int i=1;i<=q;i++)
    		printf("%d
    ",ans[i]);
    	return 0;
    }
    

      

  • 相关阅读:
    python学习===从一个数中分解出每个数字
    python学习===复制list
    Jmeter===测试案例参考
    Jmeter==HTTP信息头管理器的作用
    python实战===使用随机的163账号发送邮件
    python实战===实现读取txt每一行的操作,账号密码
    python实战===生成随机数
    python实战===输入密码以******的形式在cmd中展示
    python实战===使用smtp发送邮件的源代码,解决554错误码的问题,更新版!
    python实战===使用smtp发送邮件的源代码,解决554错误码的问题
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/UOJ195.html
Copyright © 2020-2023  润新知