题解 CF1387B1 【Village (Minimum)】
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;
}