题目链接:https://codeforces.com/contest/1246/problem/D
题意:给一棵有n个节点的有根树,要求用一棵有n个节点的竹子构造这棵树,竹子是一种每个节点只有一个子节点,只有一个没有子节点的节点的有根树。可以对竹子进行,若节点v不为根且v的父节点不为根,则将v和v的子树移到v的父节点的父节点上的操作。竹子的每个节点的编号可以自定义,问最少多少次操作会使竹子变为这棵树,输出竹子的编号,操作次数和每次操作的操作对象。
做法:很(写)有(不)意(出)思(来)的构造。首先得出结论答案的下界是n-1-dep(dep为树的深度),因为每次操作只会使一个子树内的点深度-1,也就是最多使最大深度-1。反过来思考,如果给一棵竹子,用一棵树去构造,每次操作选择一个子树将它沿某条边向下移动,则移动的次数必定为n-1-dep,因此,操作的次数也必定为n-1-dep。构造过程中,将树中最深的节点所在的链放在最后,此处参考了杜老师的代码,非常精妙,具体实现见代码。
参考代码:
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int MAXN = 100005;
vector<int> sequence, operation, son[MAXN];
int cnt, heavyson[MAXN], dep[MAXN], father[MAXN], n;
void dfs(int now)
{
sequence.push_back(now);
for (int i = 0; i < cnt; ++i)
operation.push_back(now);
cnt = 0;
for (auto v:son[now])
if (v != heavyson[now])
dfs(v);
if (heavyson[now])
dfs(heavyson[now]);
cnt++;
}
int main()
{
cin >> n;
dep[0] = 1;
for (int i = 1; i < n; ++i)
{
cin >> father[i];
son[father[i]].push_back(i);
dep[i] = dep[father[i]] + 1;
}
int maxdep = max_element(dep, dep + n) - dep;
int heavysonnode = maxdep;
while (heavysonnode)
{
heavyson[father[heavysonnode]] = heavysonnode;
heavysonnode = father[heavysonnode];
}
dfs(0);
for (int i = 0; i < n; ++i)
{
if (i != 0)
cout << " ";
cout << sequence[i];
}
cout << endl;
cout << (int) operation.size() << endl;
for (int i = 0; i < (int) operation.size(); ++i)
{
if (i != 0)
cout << " ";
cout << operation[i];
}
return 0;
}