题解:CF29D Ant on the Tree
题目链接:洛谷 Codeforces
思路
题目要求每条边只能经过两次,且要遍历每一个节点,那么行走路线就必须是欧拉序。
先考虑叶子顺序的限制。设依次要访问的叶子为 \(p_1,p_2\dots p_k\),则遍历顺序一定是先从根走到 \(p_1\),回溯到 \(lca(p_1,p_2)\),再从 \(lca(p_1,p_2)\) 走到 \(p_2\)……依此类推。考虑模拟这一过程,按顺序从每个叶子开始跳父亲,给经过的点打标记,直到跳到根或之前打过标记的点,则在合法情况下第一个打标记的点一定是 \(lca(p_{i-1},p_i)\),把经过的点倒序插入一个数组后面,全部操作完后这个数组就是路线对应的 DFS 序。
再考虑通过 DFS 序还原出欧拉序,或判断不合法。直接按照 DFS 序去搜索就可以,DFS 序的下一项是当前点的儿子就往下搜,否则回溯,在途中记录欧拉序。然而直接判某个点是不是一个点的儿子需要排序或 set 之类的东西,其导致的 \(O(n\log n)\) 复杂度在 \(n=300\) 的情况下太慢了,因此可以换个角度,判 DFS 序的下一项的父亲是不是当前点。
最后,如果搜索完了还没有遍历完 DFS 序就不合法,否则答案就是得到的欧拉序。
再分析时间复杂度。搜索的复杂度不用说;跳父亲因为跳到打过标记的点就停了,每个点只会被访问一次,所以总时间复杂度是 \(O(n)\)。
代码
#include <bits/stdc++.h>
using namespace std;
#define pii pair<int,int>
#define ll long long
const int N=305,P=1e9+7;
int n,fa[N],d[N],vis[N],k;
vector<int> a[N],ans;
void dfs1(int x)
{
for(int y:a[x])
if(y!=fa[x])
{
fa[y]=x;
dfs1(y);
}
}
void dfs2(int x)
{
ans.push_back(x);
while(k<=n&&fa[d[k]]==x)
{
int y=d[k++];
dfs2(y);
ans.push_back(x);
}
}
signed main()
{
scanf("%d",&n);
for(int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
a[x].push_back(y);
a[y].push_back(x);
}
dfs1(1);
int cnt=0;
for(int i=2;i<=n;i++)
if(a[i].size()==1)
cnt++;
while(cnt--)
{
int x;
scanf("%d",&x);
stack<int> st;
while(x&&!vis[x])
{
vis[x]=1;
st.push(x);
x=fa[x];
}
while(st.size())
{
d[++k]=st.top();
st.pop();
}
}
k=2;
dfs2(1);
if(k<=n)
printf("-1");
else
for(int x:ans)
printf("%d ",x);
return 0;
}

浙公网安备 33010602011771号