zoukankan      html  css  js  c++  java
  • [BZOJ 4573][ZJOI 2016]大♂森林

    [LOJ 2092][BZOJ 4573][UOJ 195][ZJOI 2016]大♂森林

    题意

    给定一个树序列, 初始时所有树都只有一个点, 要求支持三种操作:

    1. 区间种树(在某个特定点上长出一个子结点)
    2. 区间更改种树点(就是改上面那个操作中的「特定点」)
    3. 查询某棵树上两个点间的距离

    (nle 1 imes 10^5, qle 2 imes 10^5). 不强制在线. 长出来的点标号一致, 与种树操作的顺序一致. 保证2操作合法.

    题解

    ZJOI都是神仙题啊QAQ...

    首先这题序列上的元素是树...一棵树得占它个 (O(n)) 级别的空间所以肯定得想办法离线之后一个一个处理...

    其次我们发现好像对于已经长出来的点, 后面再怎么修改也不会和它有什么关系了. 于是我们可以把重点放在前两个操作而把 2 操作丢到最后等树形态构造完成之后再搞.

    还有一点就是其实多长出来的点并不会影响答案(因为保证 2 操作合法, 而不合法的点一定不会是合法的点的祖先), 所以其实可以考虑维护一整棵包含所有 0 操作生长出来的结点的树. 这样我们就可以避免一直添点删点的问题了.

    也就是说我们现在只剩下最辣手的操作 1 了.

    考虑从第 (i) 棵树的形态如何快速转化为第 (i+1) 棵的形态.

    如果这两棵树形态不同, 必然是一棵执行了某个操作 1 而另一个没有. 假设原生长点是 (u), 新生长点是 (v), 则在操作 1 之后, 原来长在 (u) 下面的所有点实际上都应该挂在 (v) 下面才对. 所以实际上就是一个子树移动的过程.

    子树移动? Cut一下再Link一下就完了?

    假的...

    现在的需求是将某个点下面的所有子树都移动走, 但是它有多少子树可不一定...

    我们可以放一个虚点来把它们收束起来. 预处理的时候对每个 1 操作建立一个虚点, 距离这个操作最近的挂上去的点都挂在这个虚点上. 这样如果这个 1 操作产生了子树移动, 直接把虚点切下来就好了.

    什么你说虚点会导致距离增加? 裆燃是把虚点的 size 设为 0 辣~

    一些小的注意事项

    1. 注意LCT查询LCA的正确姿势以及不同Access写法的不同副作用. ((0 ext{pts} ightarrow40 ext{pts}))
    2. 注意每个实点都有一个存在区间, 如果要把某个子树挂到 (v) 上需要检查 (v) 在哪一段中存在. 不存在的不能理会. ((40 ext{pts} ightarrow70 ext{pts}))
    3. 注意 1 号点的存在区间要初始化为 ([1,n]). ((70 ext{pts} ightarrow100 ext{pts}))

    参考代码

    #include <bits/stdc++.h>
    #define _O0 __attribute__((optimize("O0"))) // 防止沙雕编译器把this当成非空来优化
    
    const int MAXN=3e5+10;
    
    struct Query{
    	int type;
    	int time;
    	int u;
    	int v;
    	int r;
    	bool friend operator<(const Query& a,const Query& b){
    		return std::make_pair(a.type,a.time)<std::make_pair(b.type,b.time);
    	}
    };
    
    struct Dump{
    	int t;
    	int a;
    	int b;
    	int c;
    };
    
    #define lch chd[0]
    #define rch chd[1]
    #define kch chd[k]
    #define xch chd[k^1]
    
    struct Node{
    	int v;
    	int sz;
    	bool rev;
    	Node* prt;
    	Node* pprt;
    	Node* chd[2];
    	Node(int v):v(v),sz(v),rev(false),prt(NULL),pprt(NULL),chd{NULL,NULL}{}
    	inline _O0 int size(){
    		return this==NULL?0:this->sz;
    	}
    	inline void Maintain(){
    		this->sz=this->lch->size()+this->rch->size()+this->v;
    	}
    	inline _O0 void Flip(){
    		if(this!=NULL){
    			std::swap(this->lch,this->rch);
    			this->rev=!this->rev;
    		}
    	}
    	inline void PushDown(){
    		if(this->rev){
    			this->lch->Flip();
    			this->rch->Flip();
    			this->rev=false;
    		}
    	}
    };
    Node* N[MAXN];
    
    void Rotate(Node* root,int k){
    	Node* tmp=root->xch;
    	root->PushDown();
    	tmp->PushDown();
    	tmp->prt=root->prt;
    	if(root->prt==NULL){
    		tmp->pprt=root->pprt;
    		root->pprt=NULL;
    	}
    	else if(root->prt->lch==root)
    		root->prt->lch=tmp;
    	else
    		root->prt->rch=tmp;
    	root->xch=tmp->kch;
    	if(root->xch!=NULL)
    		root->xch->prt=root;
    	tmp->kch=root;
    	root->prt=tmp;
    	root->Maintain();
    	tmp->Maintain();
    }
    
    void Splay(Node* root){
    	while(root->prt!=NULL){
    		int k=root->prt->lch==root;
    		if(root->prt->prt==NULL)
    			Rotate(root->prt,k);
    		else{
    			int d=root->prt->prt->lch==root->prt;
    			Rotate(k==d?root->prt->prt:root->prt,k);
    			Rotate(root->prt,d);
    		}
    	}
    }
    
    void Expose(Node* root){
    	Splay(root);
    	root->PushDown();
    	if(root->rch){
    		root->rch->pprt=root;
    		root->rch->prt=NULL;
    		root->rch=NULL;
    		root->Maintain();
    	}
    }
    
    Node* Access(Node* root){
    	Node* dump=root;
    	Node* tmp=NULL;
    	while(root){
    		Expose(root);
    		root->rch=tmp;
    		if(tmp){
    			tmp->pprt=NULL;
    			tmp->prt=root;
    		}
    		root->Maintain();
    		tmp=root;
    		root=root->pprt;
    	}
    	Splay(dump);
    	return tmp;
    }
    
    void Evert(Node* root){
    	Access(root);
    	root->Flip();
    }
    
    Node* FindRoot(Node* root){
    	Access(root);
    	Node* ans=root;
    	while(ans->lch)
    		ans=ans->lch;
    	Splay(ans);
    	return ans;
    }
    
    int Calc(int a,int b){
    	if(FindRoot(N[a])!=FindRoot(N[b]))
    		return -1;
    	int ans=0;
    	Access(N[a]);
    	ans+=N[a]->size();
    	Node* lca=Access(N[b]);
    	ans+=N[b]->size();
    //	printf("%d
    ",ans);
    	Access(lca);
    	ans-=2*lca->size();
    //	printf("%d %d lca=%p
    ",a,b,lca);
    	return ans;
    }
    
    void Link(int x,int y){
    //	printf("link %d <- %d
    ",x,y);
    	Evert(N[y]);
    	N[y]->pprt=N[x];
    }
    
    void Cut(int x){
    //	printf("cut %d
    ",x);
    	Evert(N[1]);
    	Access(N[x]);
    	N[x]->PushDown();
    	N[x]->lch->prt=NULL;
    	N[x]->lch=NULL;
    	N[x]->Maintain();
    }
    
    int n;
    int q;
    int cnt;
    int L[MAXN];
    int R[MAXN];
    Dump D[MAXN];
    int ans[MAXN];
    int prt[MAXN];
    int alive[MAXN];
    std::vector<Query> Q[MAXN];
    
    int main(){
    	scanf("%d%d",&n,&q);
    	cnt=1;
    	N[0]=new Node(0);
    	N[1]=new Node(1);
    	Link(1,0);
    	L[1]=1,R[1]=n;
    	prt[0]=1;
    	prt[1]=-1;
    	for(int i=0;i<q;i++){
    		scanf("%d%d%d",&D[i].t,&D[i].a,&D[i].b);
    		if(D[i].t!=0)
    			scanf("%d",&D[i].c);
    		else{
    			N[++cnt]=new Node(1);
    			L[cnt]=D[i].a;
    			R[cnt]=D[i].b;
    		}
    	}
    	int last=0;
    	int cur=1;
    	for(int i=0;i<q;i++){
    		if(D[i].t==0){
    			prt[++cur]=last;
    			Link(last,cur);
    		}
    		else if(D[i].t==1){
    			int l=std::max(L[D[i].c],D[i].a),r=std::min(R[D[i].c],D[i].b);
    			if(l>r)
    				continue;
    			N[++cnt]=new Node(0);
    			Link(last,cnt);
    			prt[cnt]=last;
    			last=cnt;
    			Q[l].push_back({1,i,cnt,D[i].c,r+1});
    		}
    		else
    			Q[D[i].a].push_back({2,i,D[i].b,D[i].c,0});
    	}
    //	for(int i=0;i<=cnt;i++)
    //		printf("N[%d]=%p
    ",i,N[i]);
    	for(int i=1;i<=n;i++){
    		std::sort(Q[i].begin(),Q[i].end());
    		for(auto q:Q[i]){
    			if(q.type==1){
    				if(q.r)
    					Q[q.r].push_back({1,q.time,q.u,prt[q.u],0});
    				Cut(q.u);
    				Link(q.v,q.u);
    				prt[q.u]=q.v;
    			}
    			else{
    			//	assert(q.type==2);
    				ans[q.time]=Calc(q.u,q.v);
    			}
    		}
    	}
    	for(int i=0;i<q;i++)
    		if(D[i].t==2)
    			printf("%d
    ",ans[i]);
    	return 0;
    }
    
    

  • 相关阅读:
    Execution Contexts (执行上下文)
    OOP—ECMAScript实现详解
    requireJS入门
    SqlServer 傲娇的表变量
    CSharp进阶 引用类型引发的血案
    CSharp进阶 都是请求惹的祸
    z-index问题
    js中事件(自定义事件)
    做了个后末日朋克风的梦
    昨晚的梦
  • 原文地址:https://www.cnblogs.com/rvalue/p/10365321.html
Copyright © 2011-2022 走看看