[JZOJ3691] 【CF414E】Mashmokh's Designed tree

1 篇文章 0 订阅

题目

题目大意

给你一棵树,接下来对这棵树进行三种操作:
1、询问两点之间的距离。
2、让某个点变为它原来的第 h h h个祖先的最后一个儿子。
3、求 d f s dfs dfs序中最后一个深度为 k k k的点。


正解

第一种是Cold_Chair大爷提出来的 L C T LCT LCT维护 E T T ETT ETT的做法。
具体怎样就不说了……据说代码5000+

第二种就直接是 E T T ETT ETT了(其实这是一道ETT的板题啊)
对于入栈点(记作 l l l)打个 + 1 +1 +1,对于出栈点(记作 r r r)打个 − 1 -1 1,维护前缀和。这个前缀和相当于深度。
这里维护前缀和的方法比较巧妙,不需要区间修改,具体见程序。
对于第一个操作,找到 l u l_u lu l v l_v lv之间最小的深度。显然这个深度就是它们的 L C A LCA LCA的深度。
对于第二个操作,找第 h h h个祖先的时候先求出祖先的深度,然后在前面找最靠右的深度为它的点。显然前缀和是连续的,所以可以在 s p l a y splay splay上二分。如果它是入栈点,就是要找的那个祖先;如果是出栈点,那它的父亲就是那个祖先。
对于第三个操作,直接找最后面前缀和为 k k k的点。和操作二一样。


代码

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <ctime>
#include <cstdlib>
#include <climits>
#include <vector>
#define N 100010
#define INF 1000000000
int n,m;
vector<int> to[N];
struct Node *null,*root;
struct Node{
	Node *fa,*c[2];
	int val,sum,mx,mn;
	inline void init(int _val){
		fa=c[0]=c[1]=null;
		val=sum=mx=mn=_val;
	}
	inline void update(){
		sum=c[0]->sum+c[1]->sum+val;
		mx=max(max(c[0]->mx,c[0]->sum+val+c[1]->mx),c[0]->sum+val);
		mn=min(min(c[0]->mn,c[0]->sum+val+c[1]->mn),c[0]->sum+val);
	}
	inline bool getson(){return fa->c[0]!=this;}
	inline void rotate(){
		Node *y=fa,*z=y->fa;
		if (z!=null)
			z->c[y->getson()]=this;
		bool k=getson();
		fa=z;
 		y->c[k]=c[k^1],c[k^1]->fa=y;
		c[k^1]=y,y->fa=this;
		sum=y->sum,mx=y->mx,mn=y->mn;
		y->update();
	}
	inline void splay(Node *t){
		while (fa!=t){
			if (fa->fa!=t)
				getson()!=fa->getson()?rotate():fa->rotate();
			rotate();
		}
		if (t==null)
			root=this;
	}
} in[N],out[N],*beg,*end;
inline Node *nxt(Node *t,bool dir){
	t->splay(null);
	Node *res=t->c[dir];
	for (;res->c[dir^1]!=null;res=res->c[dir^1]);
	return res;
}
inline void push_back(Node *x){
	Node *t=root;
	for (;t->c[1]!=null;t=t->c[1]);
	t->splay(null);
	t->c[1]=x,x->fa=t;
	t->update();
}
inline Node *split(Node *l,Node *r){
	l=nxt(l,0),r=nxt(r,1);
	r->splay(null),l->splay(r);
	Node *t=l->c[1];
	l->c[1]=null,t->fa=null;
	l->update(),r->update();
	return t;
}
inline void insert(Node *t,Node *p){
	p->splay(null);
	Node *pre=nxt(p,0);
	pre->splay(p);
	pre->c[1]=t,t->fa=pre;
	pre->update(),p->update();
}
inline Node *find_last(Node *t,int k){
	t->splay(null);
	Node *x=t->c[0],*res=null;
	while (1){
		if (x->c[1]!=null && x->c[1]->mn<=k-x->c[0]->sum-x->val && k-x->c[0]->sum-x->val<=x->c[1]->mx){
			k-=x->c[0]->sum+x->val;
			x=x->c[1];
			continue;
		}
		else if (x->c[0]->sum+x->val==k)
			return x;
		x=x->c[0];
	}
	return res;
}
void dfs(int x){
	in[x].init(1);
	push_back(&in[x]);
	for (int i=0;i<to[x].size();++i)
		dfs(to[x][i]);
	out[x].init(-1);
	push_back(&out[x]);
}
int fa[N];
int main(){
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;++i){
		int num;
		scanf("%d",&num);
		for (int j=0;j<num;++j){
			int x;
			scanf("%d",&x);
			to[i].push_back(x);
			fa[x]=i;
		}
	}
	null=new Node,*null={null,null,null,0,0,-INF,INF};
	beg=new Node,beg->init(0),root=beg;
	dfs(1);
	end=new Node,end->init(0),push_back(end);
	for (int i=1;i<=m;++i){
		int op;
		scanf("%d",&op);
		if (op==1){
			int u,v,depu,depv,deplca;
			scanf("%d%d",&u,&v);
			if (u==v){
				printf("0\n");
				continue;
			}    
			in[u].splay(null),in[v].splay(&in[u]);
			if (in[v].getson()==0)
				swap(u,v);
			Node *l=nxt(&in[u],0),*r=nxt(&in[v],1);
			r->splay(null),l->splay(r);
			deplca=l->c[0]->sum+l->val+l->c[1]->mn;
			depu=l->c[0]->sum+l->val+1;
			depv=l->sum;
			printf("%d\n",depu+depv-deplca*2); 
		}
		else if (op==2){
			int v,h,anc;
			scanf("%d%d",&v,&h);
			in[v].splay(null);
			Node *tmp=find_last(&in[v],in[v].c[0]->sum+1-h);
			fa[v]=anc=(tmp->val==-1?fa[tmp-out]:tmp-in);
			Node *t=split(&in[v],&out[v]);
			insert(t,&out[anc]);
		}
		else{
			int k;
			scanf("%d",&k);
			Node *tmp=find_last(end,k+1);
			printf("%d\n",tmp->val==-1?fa[tmp-out]:tmp-in);
		}
	}
	return 0;
}

总结

L C T LCT LCT搞不了的东西,要试试 E T T ETT ETT

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值