题解: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;
}
posted @ 2026-09-17 17:00  ShanLing3  阅读(2)  评论(0)    收藏  举报
//雪花飘落效果