洛谷P3348 [ZJOI2016]大森林 [LCT]

传送门


刷了那么久水题之后终于有一题可以来写写博客了。

但是这题太神仙了我还没完全弄懂……

upd:写完博客之后似乎懂了。


思路

首先很容易想到\(O(n^2\log n)\)乘上\(O(\frac{n}{\log n})\)的巨大常数的暴力做法(雾

然后可以发现这题支持把询问抽离出来最后做,那么我们可以先只关注修改操作。

可以发现一个点在\([l,r]\)的树上连上去和在所有树上都连上去其实没有太大区别,只是修改生长节点时要判一下是否存在,其他时候其实可以每一棵树上都连一个,因为不存在的点并不会被询问到。

发现这题主要恶心在1操作(也就是更换生长节点),如果没有1操作那么所有树的形态就都一样了。

考虑用某种方法使得我们可以只维护一棵树,然后在这一棵树上面实现从\(1\)\(n\)的灵活转换。

其实也不用太过灵活,只需要能够从\(i\)快速推到\(i+1\)即可。

既然1操作恶心,那么我们重点分析1操作。

考虑\([l,r],x\)的这样一次操作,对\(l,l-1\)这两棵树会造成什么差异。假设\(l-1\)的生长节点为\(y\)

那么这就相当于把接下来所有连在\(y\)上面的节点以及它们的子树全都接到\(x\)上。(注意:不管它们是否真的有在\(x\)树上,这对答案没有影响

同理,\(r,r+1\)之间的差异相当于接回去。

那么我们只要迅速地修改一堆子树的父亲即可。

考虑在一个虚点下面连上这一堆子树,那么每次就只需要修改虚点的父亲就好了。

每当有一个新的1操作\([l,r],x\)时就新开一个虚点\(p\),表示接下来所有0操作的点都连到\(p\)上,同时\(p\)也要连到上一个虚点上,保证之前的虚点也连上了所有点。

\(p\)\(l\)树之前一直连在原虚点上,\([l,r]\)时连在\(x\)上,之后再连回去。

然后就可以从\(1\)\(n\)处理这棵树上的修改和询问,然后就做完了。

我不敢确定自己是否真的理解了做法,请发现思路上的错误的大佬指正,谢谢!


代码

注意这里没有保存一个点在原树上的父亲是谁,所以不能随便\(\text{makeroot}\)破坏LCT上的父子结构。

但是我还是不怎么明白\(\text{cut}\)函数中为什么\(lson(x)\)一定是\(x\)在原树中的父亲,希望有大佬赐教。

#include<bits/stdc++.h>
clock_t t=clock();
namespace my_std{
	using namespace std;
	#define pii pair<int,int>
	#define fir first
	#define sec second
	#define MP make_pair
	#define rep(i,x,y) for (int i=(x);i<=(y);i++)
	#define drep(i,x,y) for (int i=(x);i>=(y);i--)
	#define go(x) for (int i=head[x];i;i=edge[i].nxt)
	#define templ template<typename T>
	#define sz 404040
	typedef long long ll;
	typedef double db;
	mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
	templ inline T rnd(T l,T r) {return uniform_int_distribution<T>(l,r)(rng);}
	templ inline bool chkmax(T &x,T y){return x<y?x=y,1:0;}
	templ inline bool chkmin(T &x,T y){return x>y?x=y,1:0;}
	templ inline void read(T& t)
	{
		t=0;char f=0,ch=getchar();double d=0.1;
		while(ch>'9'||ch<'0') f|=(ch=='-'),ch=getchar();
		while(ch<='9'&&ch>='0') t=t*10+ch-48,ch=getchar();
		if(ch=='.'){ch=getchar();while(ch<='9'&&ch>='0') t+=d*(ch^48),d*=0.1,ch=getchar();}
		t=(f?-t:t);
	}
	template<typename T,typename... Args>inline void read(T& t,Args&... args){read(t); read(args...);}
	char __sr[1<<21],__z[20];int __C=-1,__zz=0;
	inline void Ot(){fwrite(__sr,1,__C+1,stdout),__C=-1;}
	inline void print(register int x)
	{
		if(__C>1<<20)Ot();if(x<0)__sr[++__C]='-',x=-x;
		while(__z[++__zz]=x%10+48,x/=10);
		while(__sr[++__C]=__z[__zz],--__zz);__sr[++__C]='\n';
	}
	void file()
	{
		#ifndef ONLINE_JUDGE
		freopen("a.in","r",stdin);
		#endif
	}
	inline void chktime()
	{
		#ifndef ONLINE_JUDGE
		cout<<(clock()-t)/1000.0<<'\n';
		#endif
	}
	#ifdef mod
	ll ksm(ll x,int y){ll ret=1;for (;y;y>>=1,x=x*x%mod) if (y&1) ret=ret*x%mod;return ret;}
	ll inv(ll x){return ksm(x,mod-2);}
	#else
	ll ksm(ll x,int y){ll ret=1;for (;y;y>>=1,x=x*x) if (y&1) ret=ret*x;return ret;}
	#endif
//	inline ll mul(ll a,ll b){ll d=(ll)(a*(double)b/mod+0.5);ll ret=a*b-d*mod;if (ret<0) ret+=mod;return ret;}
}
using namespace my_std;

int n,m;

int fa[sz],ch[sz][2],w[sz],sum[sz];
#define ls ch[x][0]
#define rs ch[x][1]
void pushup(int x){sum[x]=w[x]+sum[ls]+sum[rs];}
bool get(int x){return ch[fa[x]][1]==x;}
bool nroot(int x){return ch[fa[x]][0]==x||ch[fa[x]][1]==x;}
void rotate(int x)
{
	int y=fa[x],z=fa[y],k=get(x),w=ch[x][!k];
	if (nroot(y)) ch[z][get(y)]=x;ch[x][!k]=y;ch[y][k]=w;
	if (w) fa[w]=y;fa[y]=x;fa[x]=z;
	pushup(y);pushup(x);
}
void splay(int x){for (int y;(y=fa[x],nroot(x));rotate(x)) if (nroot(y)) rotate(get(x)==get(y)?y:x);}
int access(int x){int y=0;for (;x;x=fa[y=x]) splay(x),rs=y,pushup(x);return y;}
void link(int x,int y){splay(x);fa[x]=y;}
void cut(int x){access(x);splay(x);fa[ls]=0;ls=0;pushup(x);}
int query(int x,int y)
{
	int ret=0;
	access(x);splay(x);ret+=sum[x];
	int lca=access(y);
	splay(y);
	ret+=sum[y];
	access(lca);splay(lca);ret-=2*sum[lca];
	return ret;
}

int L[sz],R[sz]; // x点存在的区间
int pos[sz]; // x在LCT中的编号

struct hh
{
	int pos,x,y,id;
	const bool operator < (const hh &y) const { return pos==y.pos?id<y.id:pos<y.pos; }
}q[sz];int c;

int ans[sz];

int main()
{
	file();
	read(n,m);
	int ima,real,cnt; // 当前虚点、实点编号,总编号 
	ima=2;real=1;cnt=2;
	L[1]=1;R[1]=n;pos[1]=1;w[1]=sum[1]=1;
	link(2,1);
	int opt,x,y,z,l,r;
	rep(i,1,m)
	{
		read(opt);
		if (opt==0)
		{
			read(l,r);
			x=++real;pos[x]=++cnt;w[pos[x]]=sum[pos[x]]=1;
			link(pos[x],ima);
			L[x]=l,R[x]=r;
		}
		else if (opt==1)
		{
			read(l,r,x);
			l=max(l,L[x]);r=min(r,R[x]);
			if (l>r) continue;
			z=++cnt;link(z,ima);
			q[++c]=(hh){l,z,pos[x],i-m};
			q[++c]=(hh){r+1,z,ima,i-m};
			ima=z;
		}
		else read(z,x,y),q[++c]=(hh){z,pos[x],pos[y],i};
	}
	sort(q+1,q+c+1);
	memset(ans,-1,sizeof(ans));
	rep(i,1,c) 
		if (q[i].id<=0) cut(q[i].x),link(q[i].x,q[i].y);
		else ans[q[i].id]=query(q[i].x,q[i].y);
	rep(i,1,m) if (ans[i]!=-1) printf("%d\n",ans[i]);
	return 0;
}
posted @ 2019-04-05 22:02  p_b_p_b  阅读(227)  评论(0编辑  收藏  举报