【CF1137F】Matches Are Not a Child's Play(LCT)(树状数组)

传送门


题解:

考虑树上最大值和次大值,显然链接它们的链最后删除,且一定是从次大值向最大值一个一个删除。

显然我们可以把树拆成若干条链,每条链上的点的删除顺序从一个端点依次向另一个端点。

注意到拆成链之后每条链内部的点是一起删除的,而链与链之间的删除顺序由链上最大值来决定。

定最大值所在节点为根,容易发现修改就是make_root,询问的话需要知道最大值小于该链最大值的链的siz总和,以及这条链上比该点先删除的点的数量,也就是处于同一条链且深度比它大的点的数量。

LCT+BIT搞定。


代码:

#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++;
	}
	inline int get_s(char *s){
		int len=0;char c;
		while(isspace(c=gc()));
		while(s[len++]=c,!isspace(c=gc())&&c!=EOF);
		s[len]='\0';return len;
	}
	
	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;

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

int n,m;

int tr[M];
inline int add(int p,int v){for(;p<M;p+=p&-p)tr[p]+=v;}
inline int qy(int p){int r=0;for(;p;p^=p&-p)r+=tr[p];return r;}

std::vector<int> G[N];

int mx[N],siz[N];
int fa[N],son[N][2],rev[N];
inline void pushup(int u){siz[u]=siz[son[u][0]]+siz[son[u][1]]+1;}
inline void pushdown(int u){
	if(rev[u]){
		std::swap(son[u][0],son[u][1]);
		rev[son[u][0]]^=1;
		rev[son[u][1]]^=1;
		rev[u]=0;
	}
	if(son[u][0])mx[son[u][0]]=mx[u];
	if(son[u][1])mx[son[u][1]]=mx[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){
	static int q[N],qn;q[qn=1]=u;
	for(int re p=u;!isrt(p);p=fa[p])q[++qn]=fa[p];
	while(qn)pushdown(q[qn--]);
	for(int re p=fa[u];!isrt(u);Rotate(u),p=fa[u])
	if(!isrt(p))Rotate(which(p)==which(u)?p:u);
}

int now;
inline void access(int u){
	for(int re ch=0;u;u=fa[ch=u]){
		Splay(u);son[u][1]=0;pushup(u);
		add(mx[u],-siz[u]);
		add(now,siz[u]);
		son[u][1]=ch,pushup(u);
	}
}

inline void makert(int u){
	++now;access(u);Splay(u);rev[u]^=1;mx[u]=now;
}

char s[10];
inline int query(int u){
	Splay(u);return qy(mx[u])-siz[son[u][0]];
}

void dfs(int u,int p){
	fa[u]=p;mx[u]=u;
	for(int re v:G[u])if(v!=p){
		dfs(v,u);
		if(mx[v]>mx[u]){
			mx[u]=mx[v],son[u][1]=v;
		}
	}
	add(mx[u],1);
}

signed main(){
#ifdef zxyoi
	freopen("fuck_it.in","r",stdin);
#endif
	now=n=gi(),m=gi();
	for(int re i=1;i<n;++i){
		int u=gi(),v=gi();
		G[u].push_back(v);
		G[v].push_back(u);
	}dfs(n,0);
	while(m--){
		switch(get_s(s)){
			case 2:makert(gi());break;
			case 4:cout<<query(gi())<<"\n";break;
			case 7:{
				int u=gi(),v=gi();
				cout<<(query(u)<query(v)?u:v)<<"\n";
				break;
			}
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值