CF1521D Nastia Plays with a Tree 题解
一、题目:
二、思路:
这是一道非常清奇的贪心题。
考虑这样一件事情,假设我们切了\(x\)刀后把整棵树变成了一堆链,那么链的个数一定是\(x+1\)。
这样的话,我们只需要将这些链首尾相连,最终形成的树也一定是一条链,而首尾相连的操作次数也一定是\(x\)。
我们又可以发现,先切哪一刀和先连哪一条边是无所谓的。我们只需要关心哪些边需要被切来使得切的次数最小。
考虑贪心。首先我们任选一个点作为根。然后假设我们现在要处理以\(v\)为根的子树。
- 递归处理\(v\)的所有儿子。
- 设\(v\)现在有\(c\)个儿子,对\(c\)进行分类讨论。
- 若\(c\leq 1\),啥都不需要干。
- 若\(c=2\),切断\(v\)和\(v\)的父亲(如果有的话)。
- 若\(c\ge 3\),先切断\(v\)和\(v\)的父亲(如果有的话),然后随便切断\(v\)的\(c-2\)个儿子。
现在我们来感性理解一下为什么这样贪心是对的。当执行完第1步后,\(v\)的所有儿子变成了一条链的一端。对于\(v\)来说,我们需要尽可能使得\(v\)的父亲的儿子数减少,这是因为一个点的儿子数越少,操作数也会变得越少。所以当有必要时,首先考虑切\(v\)的父亲 ,再切\(v\)的儿子。
三、代码:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
inline int read(void) {
int x = 0, f = 1; char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return f * x;
}
const int maxn = 1e5 + 5;
int T, n, head[maxn], tot, top, deg[maxn];
bool vis[maxn];
pair<int, int>pa[maxn];
struct Edge {
int y, next;
bool tag;
Edge() {}
Edge(int _y, int _next, bool _tag) : y(_y), next(_next), tag(_tag) {}
}e[maxn << 1];
struct Opt{
int x1, y1, x2, y2;
}opt[maxn];
inline void connect(int x, int y) {
e[++ tot] = Edge(y, head[x], 1);
head[x] = tot;
}
void dfs(int x, int fa) {
int pos = 0;
for (int i = head[x]; i; i = e[i].next) {
int y = e[i].y;
if (y == fa) { pos = i; continue; }
dfs(y, x);
}
int cnt = 0;
for (int i = head[x]; i; i = e[i].next) {
int y = e[i].y;
if (y == fa || !e[i].tag) continue;
++ cnt;
}
if (cnt <= 1) return;
if (cnt == 2) {
if (!fa) return;
e[pos].tag = e[pos ^ 1].tag = false;
opt[++ top].x1 = x; opt[top].y1 = fa;
-- deg[x]; -- deg[fa];
return;
}
if (fa) {
e[pos].tag = e[pos ^ 1].tag = false;
opt[++ top].x1 = x; opt[top].y1 = fa;
-- deg[x]; -- deg[fa];
}
int tmp = 0;
for (int i = head[x]; i; i = e[i].next) {
int y = e[i].y;
if (y == fa || !e[i].tag) continue;
++ tmp;
if (tmp <= cnt - 2) {
e[i].tag = e[i ^ 1].tag = false;
opt[++ top].x1 = x; opt[top].y1 = y;
-- deg[x];
-- deg[y];
}
}
}
void dfs2(int x, int fa, int &to) {
int cnt = 0;
vis[x] = true;
for (int i = head[x]; i; i = e[i].next) {
int y = e[i].y;
if (y == fa || !e[i].tag) continue;
dfs2(y, x, to);
++ cnt;
}
if (!cnt) to = x;
}
int main() {
T = read();
while (T --) {
tot = 1;
memset(head, 0, sizeof head);
memset(deg, 0, sizeof deg);
memset(vis, false, sizeof vis);
n = read();
for (int i = 1; i < n; ++ i) {
int a = read(), b = read();
connect(a, b); connect(b, a);
++ deg[a]; ++ deg[b];
}
top = 0;
dfs(1, 0);
printf("%d\n", top);
top = 0;
for (int x = 1; x <= n; ++ x) {
if (deg[x] == 0 && !vis[x]) {
vis[x] = true;
pa[++ top] = make_pair(x, x);
}
if (deg[x] == 1 && !vis[x]) {
int y = 0;
dfs2(x, 0, y);
pa[++ top] = make_pair(x, y);
}
}
-- top;
for (int i = 1; i <= top; ++ i) {
opt[i].x2 = pa[i].second;
opt[i].y2 = pa[i + 1].first;
printf("%d %d %d %d\n", opt[i].x1, opt[i].y1, opt[i].x2, opt[i].y2);
}
}
return 0;
}