【ZJOI2016】大森林(LCT)

传送门


题解:

区间LCT种树

离线,考虑这棵树和下一棵树的区别。

其实就是需要把某些子树从一个点转移到另一个点。

建立虚点,把一段时间内(两次改变生长节点之间)长出来的节点用一个虚点接起来,于是在改变生长节点的时候就可以直接用LCT把子树接过去了。

没什么细节,xjb实现就行了。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

namespace IO{
	inline char gc(){
		static cs int Rlen=1<<22|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	template<typename T>
	inline T get(){
		char c;T num;
		while(!isdigit(c=gc()));num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
	inline int gi(){return get<int>();} 
}
using namespace IO;

using std::cerr;
using std::cout;
typedef std::pair<int,int> pii;
#define fi first
#define se second

cs int N=1e5+7,M=2e5+7;

namespace LCT{
	cs int N=::M+::M;
	int fa[N],siz[N],val[N],son[N][2];
	inline void pushup(int u){siz[u]=siz[son[u][0]]+siz[son[u][1]]+val[u];}
	inline bool isrt(int u){return son[fa[u]][0]!=u&&son[fa[u]][1]!=u;}
	inline bool which(int u){return son[fa[u]][1]==u;}
	inline void Rotate(int u){
		int p=fa[u],pp=fa[p],d=which(u);
		if(!isrt(p))son[pp][which(p)]=u;
		fa[u]=pp,fa[p]=u,fa[son[u][!d]]=p;
		son[p][d]=son[u][!d],son[u][!d]=p;
		pushup(p),pushup(u);
	}
	inline void Splay(int u){
		for(int re p=fa[u];!isrt(u);Rotate(u),p=fa[u])
		if(!isrt(p))Rotate(which(u)==which(p)?p:u);
	}
	inline int access(int u){
		int ch=0;for(;u;u=fa[ch=u])
		Splay(u),son[u][1]=ch,pushup(u);
		return ch;
	}
	inline void link(int u,int f){
		Splay(u),fa[u]=f;
	}
	inline void cut(int u){
		access(u),Splay(u);
		fa[son[u][0]]=0,son[u][0]=0,pushup(u);
	}
	inline int dis(int u,int v){
		int res=siz[access(u)],t=access(v);
		res+=siz[t],res-=siz[access(t)]*2;
		return res;
	}
}

int n,m;
struct Query{int id,u,v;};
std::vector<Query> qry[N];
std::vector<pii> modi[N];
int l[M],r[M],nd[M],ans[M],ct,nct,las;

signed main(){
#ifdef zxyoi
	freopen("forest.in","r",stdin);
#endif
	n=gi(),m=gi();
	ct=nd[1]=1,las=nct=2;
	LCT::val[1]=1;LCT::link(2,1);
	l[1]=1,r[1]=n;
	for(int re i=1;i<=m;++i){
		ans[i]=-1;
		switch(gi()){
			case 0:{
				LCT::link(nd[++ct]=++nct,las);
				LCT::val[nct]=1;
				l[ct]=gi(),r[ct]=gi();
				break;
			}
			case 1:{
				int l=gi(),r=gi(),x=gi();
				l=std::max(l,::l[x]);
				r=std::min(r,::r[x]);
				if(l<=r){
					++nct;
					if(l>1)LCT::link(nct,las);
					modi[l].push_back(pii(nct,nd[x]));
					modi[r+1].push_back(pii(nct,las));
					las=nct;
				}
				break;
			}
			case 2:{
				int u=gi(),x=gi(),y=gi();
				qry[u].push_back((Query){i,nd[x],nd[y]});
				break;
			}
		}
	}
	for(int re i=1;i<=n;++i){
		for(auto t:modi[i]){
			LCT::cut(t.fi);
			LCT::link(t.fi,t.se);
		}
		for(auto t:qry[i]){
			ans[t.id]=LCT::dis(t.u,t.v);
		}
	}
	for(int re i=1;i<=m;++i)if(~ans[i])cout<<ans[i]<<"\n";
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值