luogu P4006 小 Y 和二叉树

luogu

loj

可以发现度数\(< 3\)的点可以作为先序遍历的第一个点,那么就把度数\(< 3\)的编号最小的点作为第一个点.然后现在要确定它的左右儿子(或者是右儿子和父亲).我们把第一个点作为根,设\(f_x\)\(x\)子树内先序遍历第一个点的最小值,一遍树型dp求出来

做的时候要用个变量记录这个点要放左右儿子还是右儿子和父亲,如果当前这个点是上一个点的父亲(或者是第一个点),那么这个点就要放右儿子和父亲,否则放左右儿子

如果只有一个儿子\(y\),就比较两种方式哪一种得到的下一个数最小.如果要放左右儿子,那如果\(f_y<x\)\(y\)就在左子树,否则在右子树;如果要放右儿子和父亲,然后\(f_y<y\)就放右儿子,否则放父亲

如果有两个儿子,就把\(f\)更小的放在前面,也就是放左右儿子是\(f\)更小的放左儿子,放右儿子和父亲时\(f\)更小的放右儿子

确定好位置后就递归进子树处理子树答案

#include<bits/stdc++.h>
#define LL long long
#define uLL unsigned long long
#define db double

using namespace std;
const int N=1e6+10;
int rd()
{
    int x=0,w=1;char ch=0;
    while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    return x*w;
}
int to[N<<1],nt[N<<1],hd[N],tot=1;
void add(int x,int y)
{
	++tot,to[tot]=y,nt[tot]=hd[x],hd[x]=tot;
}
int n,dg[N],f[N];
void dfs(int x,int ffa)
{
	f[x]=n+1;
	int cn=0;
	for(int i=hd[x];i;i=nt[i])
	{
		int y=to[i];
		if(y==ffa) continue;
		dfs(y,x),f[x]=min(f[y],f[x]),++cn;
	}
	if(cn<2) f[x]=min(f[x],x);
}
void dd(int x,int ffa,bool ty)
{
	--dg[x];
	if(!dg[x])
	{printf("%d ",x);return;}
	if(ty==1)
	{
		printf("%d ",x);
		if(dg[x]==1)
		{
			int c1=0;
			for(int i=hd[x];i;i=nt[i])
			{
				int y=to[i];
				if(y!=ffa) c1=y;
			}
			dd(c1,x,f[c1]==c1);
		}
		else
		{
			int c1=0,c2=0;
			for(int i=hd[x];i;i=nt[i])
			{
				int y=to[i];
				if(y!=ffa) c2=c1,c1=y;
			}
			if(f[c1]<f[c2]) dd(c1,x,0),dd(c2,x,1);
			else dd(c2,x,0),dd(c1,x,1);
		}
	}
	else
	{
		if(dg[x]==1)
		{
			int c1=0;
			for(int i=hd[x];i;i=nt[i])
			{
				int y=to[i];
				if(y!=ffa) c1=y;
			}
			if(f[c1]<x)
			{
				dd(c1,x,0);
				printf("%d ",x);
			}
			else
			{
				printf("%d ",x);
				dd(c1,x,0);
			}
		}
		else
		{
			int c1=0,c2=0;
			for(int i=hd[x];i;i=nt[i])
			{
				int y=to[i];
				if(y!=ffa) c2=c1,c1=y;
			}
			if(f[c1]<f[c2])
			{
				dd(c1,x,0);
				printf("%d ",x);
				dd(c2,x,0);
			}
			else
			{
				dd(c2,x,0);
				printf("%d ",x);
				dd(c1,x,0);
			}
		}
	}
}

int main()
{
	n=rd();
	for(int i=1;i<=n;++i)
	{
		dg[i]=rd();
		for(int j=1;j<=dg[i];++j) add(i,rd());
	}
	int rt=n+1;
	for(int i=1;i<=n;++i)
		if(dg[i]<3) {rt=i;break;}
	dfs(rt,0);
	++dg[rt],dd(rt,0,1);
    return 0;
}
posted @ 2019-09-22 22:18  ✡smy✡  阅读(152)  评论(2编辑  收藏  举报