BZOJ3424: Poi2013 Multidrink

本文深入探讨了一种特殊的树形遍历问题,要求在不重复的前提下遍历所有节点,重点介绍了如何处理拥有多个子树的节点,以及如何确保路径的连贯性和可行性。

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

题目大意:给一棵树,每次步伐大小只能为1或2,要求不重复的遍历n个节点,且起点为1,终点为n

首先推荐Claris的题解,讲得还是挺详细的

其次推荐算是此题题解的英文论文,可以在看Claris题解的同时补充一下

 

不想看英文论文的就可以看我的补充了....

1.首先证明当一个节点拥有三个或以上的non-trivial的毛毛虫时是无解的

              假设我们给予这种情况最大的自由度,即遍历这段子树的时候可以任选起点为根或深度为1的点

              但是无论如何,当我们进入第三条non-trivial的毛毛虫时,入口一定是深度为1的点,并且此时根也已经走过了,所以这时一定是出不来的,路径会断在这颗子树里

             又因为n节点一定在主干上,所以不会出现路径断在里面还有解的情况

2.接着证明为什么两个B节点之间必须有FREE节点

             因为遍历一个B节点的子树时,一定是从主干中的上一个点进入一个深度为1的点,然后遍历完这个毛毛虫再经过B节点进入另一个毛毛虫,所以出来的时候一定在B节点的下一个主干节点

            假设两个B节点中间都是A且非FREE节点,那么永远从一个节点的子树出来的时候都会走到下一个主干节点,当最后一次走到B节点上时,就无解了!

            而如果有FREE节点的存在,相当于可以“缓一步”,变成可以重新优先进入深度为1的节点了,这样才有解

3.遍历一个主干节点的子树的具体方法

           这步卡死我了,Claris一句话就略过了,留了个图看起来非常简单,结果我根本写不出来TAT

           大概思路就是这样,首先把两条毛毛虫的主体(即去掉叶子节点)按某种顺序连接起来,具体方法就是拿一个数组记录从当前节点到毛毛虫的尾巴路径上点的编号,如果还有另一条毛毛虫,就将此条毛毛虫翻转,这样使得这两条毛毛虫首位相接

           然后考虑毛毛虫如何遍历,假设不考虑起始点和终点,应该从第一个点开始,然后遍历第二个点的腿,第三个点,第四个点的腿.....

          所以当有起始限制时,我们就在序列中找到开头,然后根据结尾在开头的左右决定遍历顺序,extend函数就是遍历“腿”的过程

 

嗯,差不多补充完毕了.....

 

#include<iostream>
#include<cstdio>
#include<algorithm>
#define N 500010
using namespace std;
int to[N<<1],nxt[N<<1],pre[N],cnt,du[N];
void ae(int ff,int tt)
{
	cnt++;
	to[cnt]=tt;
	nxt[cnt]=pre[ff];
	pre[ff]=cnt;
}
int fa[N];
void build(int x)
{
	int i,j;
	for(i=pre[x];i;i=nxt[i])
	{
		j=to[i];
		if(j==fa[x]) continue;
		fa[j]=x;
		build(j);
	}
}
bool checkchain(int x,int ff)
{
	int i,j,total=0;
	for(i=pre[x];i;i=nxt[i])
	{
		j=to[i];
		if(j==ff||du[j]==1) continue;
		if(!checkchain(j,x)) return false;
		total++;
		if(total==2) return false;
	}
	return true;
}
int a[N],tot,sub[N];
bool spc[N],fre[N];
void cal(int x)
{
	int i,j;
	for(i=pre[x];i;i=nxt[i])
	{
		j=to[i];
		if(spc[j]) continue;
		fre[x]=false;
		if(du[j]==1) continue;
		sub[x]++;
		if(sub[x]==3) {puts("BRAK");exit(0);}
		if(!checkchain(j,x)) {puts("BRAK");exit(0);}
	}
}
int sbson(int x)
{
	int i,j;
	for(i=pre[x];i;i=nxt[i])
	{
		j=to[i];
		if(!spc[j]) return j;
	}
}
int yzson(int x)
{
	int i,j;
	for(i=pre[x];i;i=nxt[i])
	{
		j=to[i];
		if(!spc[j]&&du[j]==1) return j;
	}
	return sbson(x);
}
int diffson(int x,int xx)
{
	int i,j;
	for(i=pre[x];i;i=nxt[i])
	{
		j=to[i];
		if(!spc[j]&&j!=xx) return j;
	}
}
int ans[N],cn;
int q[N],t;
int b[N],tt,FA[N];
int left(int x)
{
	if(x==1) return tt;
	return x-1;
}
int right(int x)
{
	if(x==tt) return 1;
	return x+1;
}
int TO[N<<1],NXT[N<<1],PRE[N],CNT;
void AE(int ff,int tt)
{
	FA[tt]=ff;
	CNT++;
	TO[CNT]=tt;
	NXT[CNT]=PRE[ff];
	PRE[ff]=CNT;
}
void findchain(int x,int ff)
{
	t++;q[t]=x;
	int i,j;
	for(i=pre[x];i;i=nxt[i])
	{
		j=to[i];
		if(j==ff) continue;
		if(du[j]==1) AE(x,j);
		else findchain(j,x);
	}
}
void extend(int x,int A,int B)
{
	int i,j;
	for(i=PRE[x];i;i=NXT[i])
	{
		j=TO[i];
		if(j==A||j==B) continue;
		cn++;ans[cn]=j;
	}
}
void getpath(int x,int S,int T)
{
	cn++;ans[cn]=S;
	if(S==T)return;
	int i,j,A,B;
	t=1;q[1]=x;
	for(i=pre[x];i;i=nxt[i])
	{
		j=to[i];
		if(spc[j]) continue;
		if(du[j]==1) AE(x,to[i]);
		else
		{
			reverse(q+1,q+t+1);
			findchain(j,x);
		}
	}
	for(i=1;i<=t;i++)
	{
		b[i]=i&1?q[i]:-q[i];
		b[2*t-i+1]=-b[i];
	}
	tt=0;
	for(i=1;i<=2*t;i++)
	{
		if(b[i]<0&&!PRE[-b[i]]) continue;
		tt++;
		b[tt]=b[i];
	}
	if(du[S]>1) A=S;
	else A=-FA[S];
	if(du[T]>1) B=T;
	else B=-FA[T];
	for(i=1;i<=tt;i++)
	if(b[i]==A) break;
	if(A<0) extend(-A,S,T);
	if(b[left(i)]==B)
	{
		for(j=right(i);b[j]!=B;j=right(j))
		if(b[j]>0) cn++,ans[cn]=b[j];
		else extend(-b[j],0,0);
	}
	else
	{
		for(j=left(i);b[j]!=B;j=left(j))
		if(b[j]>0) cn++,ans[cn]=b[j];
		else extend(-b[j],0,0);
	}
	if(B<0&&A!=B)
	extend(-B,S,T);
	cn++;
	ans[cn]=T;
}
int main()
{
	int n;
	int i,j,x,y;
	scanf("%d",&n);
	for(i=1;i<n;i++)
	{
		scanf("%d%d",&x,&y);
		ae(x,y);ae(y,x);
		du[x]++;du[y]++;
	}
	build(1);
	i=n;
	while(i!=1) tot++,a[tot]=i,i=fa[i];
	tot++;a[tot]=1;
	reverse(a+1,a+tot+1);
	for(i=1;i<=tot;i++) spc[a[i]]=true;
	for(i=1;i<=tot;i++)
	{
		fre[a[i]]=true;
		cal(a[i]);
	}
	bool fl=false,never=true;
	for(i=1;i<=tot;i++)
	{
		if(fre[a[i]]) fl=true;
		else if(sub[a[i]]==2)
		{
			never=false;
			if(fl) fl=false;
			else {puts("BRAK");return 0;}
		}
	}
	if(!fl&&!never) {puts("BRAK");return 0;}
	int S,T;
	if(fre[1]) S=1,T=1;
	else S=1,T=sbson(1);
	getpath(1,S,T);
	for(i=2;i<=tot;i++)
	{
		x=a[i];
		if(fre[x]) S=T=x;
		else if(spc[T])
		{
			S=yzson(x);
			if(sub[x]!=2) T=x;
			else T=diffson(x,S);
		}
		else S=x,T=yzson(x);
		getpath(x,S,T);
	}
//	for(i=1;i<=n;i++)cout<<ans[i]<<' ';	
	if(ans[n]!=n) {puts("BRAK");return 0;}
	for(i=1;i<=n;i++)
	printf("%d\n",ans[i]);
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值