【BZOJ】4573: [Zjoi2016]大森林 -LCT&虚点

本文详细解析了BZOJ4573题目的解题思路,通过离线操作和轻重链剖分(LCT)技巧,将复杂的问题转化为区间更新和查询,实现高效求解。介绍了如何使用虚点和区间连接来简化树状结构的更新过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门:bzoj4573


题解

观察到操作中没有撤回,对于每颗树,节点一旦出现位置就固定了,所以考虑离线操作,将操作变为左右端点的加入和撤回,扫到对应的树时回答询问。

问题的关键在于:然每棵树对应着不同的生长节点,但每次新增叶子节点的操作完全是等价的,所以考虑对于每一次1操作新建一个值为0的虚点,将与下一次1操作间隔中的新增节点都连在这个虚点上。而1操作就转变成了在 [ l , r ] [l,r] [l,r]区间内将虚点连在对应的节点下, [ 1 , l − 1 ] ∪ [ r + 1 , n ] [1,l-1]\cup[r+1,n] [1,l1][r+1,n]区间内将虚点连在上一个虚点下。

记录一下每个实点对应标号和合法区间即可。


代码

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;

int n,m,cur,id[N],lf[N],rt[N],tot;
int ans[N<<1];

inline char gc()
{
	static char buf[N];
	static int p1=0,p2=0;
	if(p1==p2) p1=0,p2=fread(buf,1,N,stdin);
	if(p1==p2) return EOF;
	return buf[p1++];
}

char cp;
inline void rd(int &x)
{
	cp=gc();x=0;
	for(;!isdigit(cp);cp=gc());
	for(;isdigit(cp);cp=gc()) x=(x<<3)+(x<<1)+(cp^48);
}

struct qr{
	int pos,typ,x,y;
	bool operator<(const qr&ky)const{
	     if(pos!=ky.pos) return pos<ky.pos;
	     return typ<ky.typ;
	}
}q[N<<1];

namespace LCT{
   #define F(x) t[(x)].fa
   #define lc(x) t[(x)].ch[0]
   #define rc(x) t[(x)].ch[1]
   #define pushup(x) t[(x)].ss=t[lc((x))].ss+t[rc((x))].ss+t[(x)].v
   #define notrt(x) (F((x))&&(lc(F((x)))==(x) || rc(F((x)))==(x)))
   
   int cnt;
   
   struct node{
   	   int ch[2],fa,v,ss;
   	   inline void itia(){v=ss=1;}
   }t[N<<1];
   
   inline void rotate(int x)
   {
   	   int y=F(x),z=F(y),pr=(rc(y)==x);
   	   t[y].ch[pr]=t[x].ch[pr^1];F(t[y].ch[pr])=y;
   	   F(x)=z;if(notrt(y)) t[z].ch[rc(z)==y]=x;
   	   F(y)=x;t[x].ch[pr^1]=y;
   	   pushup(y);
   }
   
   inline void splay(int x)
   {
   	    int y,z;
   	    for(;notrt(x);rotate(x)){
   	    	y=F(x);z=F(y);
   	    	if(notrt(y)) 
			  ((rc(y)==x)^(rc(z)==y))?rotate(x):rotate(y);
   	    }
   	    pushup(x);
   }
   
   inline void lk(int x,int y){splay(x);F(x)=y;}
   
   inline int access(int x)
   {
   	    splay(x);rc(x)=0;pushup(x);
   	    for(;F(x);x=F(x)){
   	    	splay(F(x));rc(F(x))=x;pushup(x);
   	    }
   	    return x;
   }
   
   inline void cutt(int x)
   {
   	   access(x);splay(x);
	   F(lc(x))=0;lc(x)=0;pushup(x);
   }
}

using namespace LCT;

int main(){
	int i,j,op,x,y,z;
	rd(n);rd(m);
    id[1]=++cnt;t[cnt].itia();
    lf[1]=1;rt[1]=n;
	cur=++cnt;lk(2,1);n=1;
	for(i=1;i<=m;++i){
	   rd(op);rd(x);rd(y);
	   if(!op){
	   	  id[++n]=++cnt;t[cnt].itia();
		  lf[n]=x;rt[n]=y;
	   	  q[++tot]=(qr){0,-1,cnt,cur};
	   }else if(op==1){
	   	  rd(z);x=max(x,lf[z]);y=min(y,rt[z]);
	   	  if(x<=y){
	   	  	 cnt++;
	   	  	 if(x>1) lk(cnt,cur);
	   	  	 q[++tot]=(qr){x,0,cnt,id[z]};
	   	  	 q[++tot]=(qr){y+1,0,cnt,cur};
	   	  	 cur=cnt;
	   	  }
	   }else{rd(z);q[++tot]=(qr){x,i,id[y],id[z]};}
	}
	sort(q+1,q+tot+1);
	memset(ans,0xff,sizeof(ans));
	for(i=j=1;i<=tot;i=j){
	    for(;j<=tot && q[j].pos==q[i].pos;++j){
	    	if(q[j].typ>0){
	    		x=q[j].x;y=q[j].y;z=q[j].typ;
	    		access(x);splay(x);ans[z]=t[x].ss;
	    		x=access(y);splay(y);ans[z]+=t[y].ss;
	    		access(x);splay(x);ans[z]-=(t[x].ss<<1);
	    	}else{
	    		cutt(q[j].x);lk(q[j].x,q[j].y);
	    	}
	    }
	}
	for(i=1;i<=tot;++i) if(ans[i]!=-1) printf("%d\n",ans[i]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值