题面:
借鉴了榜单前列的部分佬的代码,写出了一些思路
中文题意:
给一颗树,节点最多2e5个,1是根
定义芽节点为子节点都是叶子节点的非根节点
可以把任意芽节点和它的父亲节点的边删掉
然后把这个芽节点连带着它的子节点们
一起连到其他的节点上
这个操作可以任意次执行
问能得到的最小叶子节点数是多少
题解:
首先:
移动一个芽节点可以使叶子节点减少一个
(即移动到一个叶子节点上)
所有的叶子节点的父节点
都是同一个芽节点时除外,
此时叶子节点数一定最少
推论:
所有的非根节点的叶子节点的父节点
都可以在经过一定的操作后变为芽节点
某些子节点中没有叶子节点的节点
也可以经过一定的操作后变为芽节点
因为它的子节点可能在
经过一定的操作后变为叶子节点
(如某个节点的子节点的子节点是芽节点
在芽节点移走后,
这个节点的子节点变为叶子节点,
这个节点变为芽节点)
综上,
可作为芽节点的节点的父节点的父节点也可作为芽节点。
所以只要找出所有
子节点中可能有叶子节点的节点的
是叶子节点或可能变成叶子节点的子节点数量
这个节点的贡献就是这个 ↑ 数量-1
其它节点贡献是0
算出所有节点的贡献和
再加上1,就是答案
(如果根节点有或可能有叶子节点,
那么答案多减了1,因为根节点不能移动
如果没有,
那么最后一个芽节点的移动
不会让叶子节点数-1, 也需要+1)
ac代码:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int n, m, t, k, a, b, c, d, mark, an, oT_To, x, y, z, ans, MARK;
vector <vector <int> > ve;
bool dfs(int now, int fa) {
int cnt = 0;
bool mark = 0, nm = 0;
for (int i : ve[now]) {
if (i == fa) continue;
nm = dfs(i, now);
cnt += !nm;
mark |= !nm;
}
ans += max(0, cnt - 1);
return mark;
}
void solve() {
scanf ("%d", &n );
ve = vector<vector<int> > (n+2);
for (int i = 1; i < n; ++i) {
scanf ("%d%d", &a, &b);
ve[a].emplace_back(b);
ve[b].emplace_back(a);
}
ans = 1;
dfs(1, 0);
printf ("%d\n", ans);
}
int main () {
// ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
// oT_To = 1;
scanf ("%d", &oT_To);
while (oT_To--) {
solve();
}
}