简要题意:
给定一棵树(或基环树),每个节点只能至多回溯一次,求遍历整棵树的最小字典序。
基环树概念:树多一条边,即树上出现且仅出现一个环。
作为 NOIP2018 Day2 T1 \texttt{NOIP2018 Day2 T1} NOIP2018 Day2 T1,确实有些难度。不过我们从部分分开始想。
对于 60 % 60 \% 60% 的数据,给定的是树。
那么就有这样的性质:
-
以 1 1 1 为根,并从 1 1 1 开始遍历肯定是最优的。(因为 1 1 1 的字典序最小)
-
假设 u u u 的父亲节点是 v v v,那么 u u u 想要回溯到 v v v 的条件应该是 u u u 的所有子树已经遍历完毕。因为如果没有遍历完就回溯到 v v v,后面不可能有其它边伸向这个子树。(当然除非是基环树)
根据上面两条性质,我们草稿一些程序步骤:
-
对于当前节点,按照其儿子的大小进行遍历。即 先遍历第 1 1 1 小,然后第 2 2 2 小 ⋯ ⋯ \cdots \cdots ⋯⋯
-
当前是叶子节点,那么就结束,回溯到第 1 1 1 步。
最后统计答案即可,其实就是一个 dfs \text{dfs} dfs 遍历树的变版。
时间复杂度: O ( n + m ) O(n+m) O(n+m).
实际得分: 60 p t s 60pts 60pts.
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
const int N=5e3+1;
inline int read(){
char ch=getchar();int f=1;while(ch<'0' || ch>'9') {
if(ch=='-') f=-f; ch=getchar();}
int x=0;while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;}
int n,m,fa[N]; //fa[i] 表示 i 的父亲节点
vector<int>G[N]; //图
vector<int>son[N]; //儿子节点
inline void dfs_fa(int dep,int bs) {
//表示 bs 是 dep 的父亲
// printf("%d %d\n",dep,bs);
fa