CF1387B1 题解

题目大意

给你一棵树,你可以把所有节点重排,不能回到原来的位置,移动一个点从 \(a\)\(b\) 的代价是 \(2\) 点之间的边数。

题解

贪心的思路很显然,因为要总移动最小,那么一个点肯定只会和自己相邻或者隔2个点的点连边。

接下来考虑对于一个节点,他有若干的子节点,那么他会有一个子节点和他互换,其他的子节点相互互换。

但是这个的奇偶之类的细节很难处理。麻烦的死。

那可以换一种思路去考虑,你考虑先把每个节点第一次遍历的子节点和他互换,并用 \(flag[]\) 标记上。

最后从 \(1 \to n\) 跑一遍,让所有的子节点围成一个环,不让他们彼此互换,把他们围成一个环就很好处理的,这个地方一看代码就懂了。

代码

#include <bits/stdc++.h>
using namespace std;
// #define int long long
#define Rep(x, a, b) for(int x = a; x <= b; ++ x)
#define Dep(x, a, b) for(int x = a; x >= b; -- x)
#define Next(i, x) for(int i = head[x]; i; i = e[i].nxt)
int read() {
    char c = getchar(); int f = 1, x = 0;
    while(c < '0' || c > '9') { if(c == '-') f = -1; c = getchar(); }
    while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
    return x * f;
}
const int maxn = 1e5 + 10;
int head[maxn], cnt, flag[maxn];
struct node { int to, nxt; } e[maxn << 1];
void add(int x, int y) { e[++ cnt] = (node) {y, head[x]}; head[x] = cnt; }
void Dfs(int x, int fa) {
    Next(i, x) {
        int v = e[i].to;
        if(v == fa) continue ;
        Dfs(v, x);
        if(!flag[x] && !flag[v]) { flag[x] = v, flag[v] = x;}
    }
}
int main() {
    int n = read(), ans = 0;
    Rep(i, 2, n) { int u = read(), v = read(); add(u, v), add(v, u); }
    Dfs(1, 0);
    Rep(i, 1, n) {
        if(flag[i]) { ++ ans; continue; }
        int tmp = e[head[i]].to;
        flag[i] = flag[tmp];
        flag[tmp] = i; ans += 2;
    }
    printf("%d\n", ans);
    Rep(i, 1, n) printf("%d ", flag[i]);
    return 0;
}