传送门
首先能发现一个性质:如果一棵子树没有花,那么这棵子树不分叉
根据这个可以发现,若一个点的所有子树都变成链,那么需要挂的花的个数是子树数-1
有了这个可以树型DP,任选一个点为根,根是要挂花的,DP出所有点的答案
但是如果根不同,答案也可能不同
所以枚举所有点为根的情况,
O
(
n
)
O(n)
O(n)跑DP
复杂度为
O
(
n
2
)
O(n^2)
O(n2)
不行,考虑优化换根操作
这是一个非常妙的技巧
任选一个点为根时,先把他为根的答案统计,再把他的数据传到儿子上,接下来就以儿子为根做DP
具体说不清楚,看代码
Code:
#include <bits/stdc++.h>
#define maxn 1000010
using namespace std;
struct Edge{
int to, next;
}edge[maxn << 1];
int num, head[maxn], dp[maxn], n, ans;
inline int read(){
int s = 0, w = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48) ;
return s * w;
}
void addedge(int x, int y){ edge[++num] = (Edge) { y, head[x] }; head[x] = num; }
void dfs1(int u, int pre){//处理以1为根的答案
int cnt = 0;
for (int i = head[u]; i; i = edge[i].next){
int v = edge[i].to;
if (v != pre){
dfs1(v, u);
dp[u] += dp[v];
cnt += !dp[v];
}
}
dp[u] += max(cnt - 1, 0);
}
void dfs2(int u, int pre){
int sum = 0, cnt = 0;
for (int i = head[u]; i; i = edge[i].next){//计算以当前点为根的答案
int v = edge[i].to;
sum += dp[v];
cnt += !dp[v];
}
ans = min(ans, 1 + (sum += max(cnt - 1, 0)));
for (int i = head[u]; i; i = edge[i].next){
int v = edge[i].to;
if (v != pre){
dp[u] = sum - dp[v];//换根,减掉即将成为根的点的贡献
if (!dp[v] && cnt > 1) --dp[u];/
dfs2(v, u);
}
}
}
int main(){
n = read();
if (n == 1) return puts("0"), 0;
for (int i = 1; i < n; ++i){
int x = read(), y = read();
addedge(x, y); addedge(y, x);
}
dfs1(1, 0);
ans = n;
dfs2(1, 0);
printf("%d\n", ans);
return 0;
}