【清华集训2017】【LOJ2324】【洛谷P4006】小 Y 和二叉树(贪心)

本文介绍了一种通过寻找度数小于三的节点并利用贪心策略来确定最佳中序遍历的方法。该方法使用旋转操作保持树的平衡,并通过交换左右子树来优化遍历顺序。

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

LOJ传送门

洛谷传送门


解析:

其实仔细想一想,这道题其实需要支持的操作就是交换左右儿子和Splay里面的Rotate,然后求最小中序遍历。

显然Rotate是不会改变中序遍历的,重点其实就是交换左右儿子。

我们找到第一个度数小于三的点,显然它就是最优解中极左链的末端。

处理出以它为根的时候每个点子树中序遍历的最小开头。

然后贪心选择最小的那边,考虑一个作为父亲另一个作为右儿子的情况就行了。

我们在 g e t a n s getans getans里面决定父亲和右儿子,在 g e t o r i getori getori里面决定左儿子和右儿子,感觉写的还是比较好懂的?可以看一下代码。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define gc get_char
#define cs const

namespace IO{
	inline char get_char(){
		static cs int Rlen=1<<20|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++; 
	}
	
	inline int getint(){
		re char c;
		while(!isdigit(c=gc()));re int num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
}
using namespace IO;

using std::cout;
using std::cerr;

cs int N=1e6+6;
int n;
int p[N][3],d[N];
int mn[N];

int st[N],top;

void dfs(int u,int fa){
	int y=0,z=0;
	for(int re i=0;i<d[u];++i)if(p[u][i]^fa)dfs(p[u][i],u),(y?z:y)=p[u][i];
	if(y&&z)mn[u]=std::min(mn[y],mn[z]);
	else if(y)mn[u]=std::min(u,mn[y]);
	else mn[u]=u;
} 

void get_ori(int u,int fa){
	int y=0,z=0;
	for(int re i=0;i<d[u];++i)if(p[u][i]^fa)(y?z:y)=p[u][i];
	if(y&&z){
		if(mn[y]>mn[z])std::swap(y,z);
		get_ori(y,u),st[++top]=u,get_ori(z,u);
	}
	else if(y){
		if(mn[u]==u)st[++top]=u,get_ori(y,u);
		else get_ori(y,u),st[++top]=u;
	}
	else st[++top]=u;
}

void get_ans(int u,int fa){
	st[++top]=u;
	int y=0,z=0;
	for(int re i=0;i<d[u];++i)if(p[u][i]^fa)(y?z:y)=p[u][i];
	if(y&&z){
		if(mn[y]>mn[z])std::swap(y,z);
		get_ori(y,u),get_ans(z,u);
	}
	else if(y){
		if(mn[y]<y)get_ori(y,u);
		else get_ans(y,u);
	}
}

signed main(){
	std::ios::sync_with_stdio(false);
	n=getint();
	for(int re i=1;i<=n;++i)
	for(int re j=(d[i]=getint())-1;~j;--j)p[i][j]=getint();
	for(int re i=1;i<=n;++i)if(d[i]<3){
		dfs(i,0);
		get_ans(i,0);
		for(int re i=1;i<=n;++i)cout<<st[i]<<" ";
		break;
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值