Luogu P3549 [POI 2013] MUL-Multidrink 题解

P3549 [POI 2013] MUL-Multidrink

非常 tricky 的一道题,模拟赛拼尽全力无法战胜,写篇题解记录一下。

容易理解的直接构造法。

按原题限制随便跳会破坏很多性质,几乎无法定向构造。考虑最后必须到 n,每经过一个点,必须要遍历其所有子树,而从 1n 至少会经过之间的路径,路径上的节点可以调整为依次经过,所以考虑把 1n 的路径提出来,每次考虑子树内的构造,走完之后再走到路径上下一个节点。

考虑一个点的子树走到下一个点的子树,只会从这个点子树的根或一个儿子走到另一个点子树的根或一个儿子。

考虑从一个点的子树走出来,结合决策包容性,发现能从根节点节点走出来就从根节点走,否则就从根节点的儿子走出来。发现此时已经不存在决策了,对每个点记录能否到自己和自己儿子,按照优先级决策,可以直接构造。

观察每一次的操作,我们发现就是要实现对于每个节点的子树儿子进自己出或自己进儿子出的构造。考虑把这些组合成一个操作。

考虑无解的情况,以某个节点自己进儿子出为例。如果存在两棵大小大于 1 的子树,进入其中一棵子树回来的时候这个节点和对应子树的子节点都已经被访问过,无解。且在任意情况下,无论怎么遍历,无解的依旧无解。

以某个节点自己进儿子出为例。由上述分析有解时每个点只会有一个大小大于 1 的子树,大小为 1 的子树可以在儿子进自己出地访问完这棵子树后顺次访问,自己进儿子出访问显然无解。于是归约为了更小的问题,递归构造。时间复杂度 O(n)

#include <bits/stdc++.h>
using namespace std;
struct edge
{
	int v,nxt;
}e[4000001];
int n,u,v,h[2000001],fa[2000001],siz[2000001],ans[2000001],st[2000001],tol=0,mx=0,cnt=0,top=0;
bool vis[2000001];
void cstr_sf(int,int);
void cstr_fs(int,int);
void add_edge(int u,int v)
{
	e[++cnt].nxt=h[u];
	e[cnt].v=v;
	h[u]=cnt;
} 

void end()
{
	printf("BRAK\n");
	exit(0);
}

void cstr_sf(int x,int pr)
{
	vector<int>ss,sl;
	for(int i=h[x];i;i=e[i].nxt)
	    if(e[i].v!=pr&&!vis[e[i].v]&&siz[e[i].v]==1)ss.push_back(e[i].v);
	    else if(e[i].v!=pr&&!vis[e[i].v])sl.push_back(e[i].v);
	if(sl.size()>=2)end();
	for(int i=0;i<ss.size();i++)ans[++tol]=ss[i];
	for(int i=0;i<sl.size();i++)cstr_fs(sl[i],x);
	ans[++tol]=x;
}

void cstr_fs(int x,int pr)
{
	ans[++tol]=x;
	vector<int>ss,sl;
	for(int i=h[x];i;i=e[i].nxt)
	    if(e[i].v!=pr&&!vis[e[i].v]&&siz[e[i].v]==1)ss.push_back(e[i].v);
	    else if(e[i].v!=pr&&!vis[e[i].v])sl.push_back(e[i].v);
	if(sl.size()>=2)end();
	for(int i=0;i<sl.size();i++)cstr_sf(sl[i],x);
	for(int i=0;i<ss.size();i++)ans[++tol]=ss[i];
}

void dfs(int x,int pr)
{
	siz[x]=1,fa[x]=pr;
	for(int i=h[x];i;i=e[i].nxt)
	    if(e[i].v!=pr)dfs(e[i].v,x),siz[x]+=siz[e[i].v];
}

bool cstr(int x,int pr,bool flag)
{
	vector<int>ss,sl;
	for(int i=h[x];i;i=e[i].nxt)
	    if(e[i].v!=pr&&!vis[e[i].v]&&siz[e[i].v]==1)ss.push_back(e[i].v);
	    else if(e[i].v!=pr&&!vis[e[i].v])sl.push_back(e[i].v);
	if(x==n)
	   {
	   	if(siz[x]==1)ans[++tol]=x;
	   	else
	   	   {
	   	   	if(!flag)end();
	   	   	cstr_sf(x,pr);
		   }
		return 0;
	   }
	bool tf=0;
	if(ss.empty()&&sl.empty())ans[++tol]=x,tf=1;
	else if(!flag)
	   {
	   	if(sl.size()>=2)end();
	   	ans[++tol]=x,tf=0;
	   	for(int i=0;i<sl.size();i++)cstr_sf(sl[i],x);
	    for(int i=0;i<ss.size();i++)ans[++tol]=ss[i];
	   }
	else
	   {
	   	if(sl.size()==0)
	   	   {
		   for(int i=0;i<ss.size();i++)ans[++tol]=ss[i];
		   ans[++tol]=x,tf=1;
	       }
	   	else if(sl.size()==1)
	   	   {
		   for(int i=0;i<ss.size();i++)ans[++tol]=ss[i];
		   for(int i=0;i<sl.size();i++)cstr_fs(sl[i],x);
		   ans[++tol]=x,tf=1;
	       }
	   	else if(sl.size()==2)
	   	   {
	   	   cstr_fs(sl[0],x),ans[++tol]=x,tf=0,cstr_sf(sl[1],x);
	   	   for(int i=0;i<ss.size();i++)ans[++tol]=ss[i];
		   }
	   	else end();
	   }
	return tf;
}

int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n-1;i++)scanf("%d%d",&u,&v),add_edge(u,v),add_edge(v,u);
	dfs(1,0);
	int x=n;
	while(x)vis[x]=1,st[++top]=x,x=fa[x];
	bool tf=0;
	for(int i=top;i>=1;i--)tf=cstr(st[i],0,tf);
	for(int i=1;i<=tol;i++)printf("%d\n",ans[i]);
	return 0;
}
posted @   w9095  阅读(4)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示