最开始想的是贪心瞎搞,拿到了 52pts 后以为有前途,结果一看讨论区发现假了。
正解是树形dp吼。
可以先看一下 洛谷的第一篇题解。写的很好,但是他写的一些东西我开始看不懂。因此在此记录一些问题与我的理解。
#include <bits/stdc++.h>
#define rep(i, a, b) for(register int i = a; i <= b; ++i)
using namespace std;
constexpr int N = 3e5 + 5;
bool vis[N];
int n, u, v, L, R, father[N], son[N], dp[N];
vector<int> e[N];
void check(int s, int k) {
dp[s] = son[s] - k;
for(const register int to : e[s]) {
if(to == father[s]) continue;
check(to, k);
dp[s] += max(dp[to], 0);
}
}
void dfs(int s, int fa) {
father[s] = fa;
for(const register int to : e[s]) {
if(to == fa) continue;
++son[s];
dfs(to, s);
}
}
int main() {
cin >> n;
rep(i, 1, n - 1) {
cin >> u >> v;
e[u].push_back(v);
e[v].push_back(u);
}
dfs(1, 0);
L = 0, R = n + 1;
while(L <= R) {
int mid = L + R >> 1;
check(1, mid);
if(dp[1] <= 0) {
R = mid - 1;
}
else {
L = mid + 1;
}
}
cout << L;
return 0;
}\(dp_i\) 的含义是染黑 \(i\)
然后这里就分两种情况。如果一个节点的 \(dp\)
这就是check部分为什么不能这么写。
void check(int s, int k) {
dp[s] = max(son[s] - k, 0);//dp_s即使是负的也有意义,因此错误
for(const register int to : e[s]) {
if(to == father[s]) continue;
check(to, k);
dp[s] += dp[to];
}
}然后是关于贪心为啥不行。我的贪心代码如下。
#include <bits/stdc++.h>
#define rep(i, a, b) for(register int i = a; i <= b; ++i)
using namespace std;
constexpr int N = 3e5 + 5;
bool vis[N];
int n, u, v, L, R, dep[N], father[N], sum[N], son[N];
vector<int> e[N];
inline bool check(int s, int k) {
bool ans = true;
if(k * dep[s] < sum[s] + son[s]) {
ans = false;
}
for(const register int to : e[s]) {
if(to == father[s]) continue;
if(!check(to, k)) {
ans = false;
}
}
return ans;
}
void dfs(int s, int fa) {
father[s] = fa;
dep[s] = dep[fa] + 1;
for(const register int to : e[s]) {
if(to == fa) continue;
++son[s];
}
for(register int to : e[s]) {
if(to == fa) continue;
sum[to] = sum[s] + son[s];
dfs(to, s);
}
}
int main() {
cin >> n;
rep(i, 1, n - 1) {
cin >> u >> v;
e[u].push_back(v);
e[v].push_back(u);
}
dfs(1, 0);
L = 0, R = n + 1;
while(L <= R) {
int mid = L + R >> 1;
if(check(1, mid)) {
R = mid - 1;
}
else {
L = mid + 1;
}
}
cout << L;
return 0;
}疑似还挺对的,但其实一点也不对。hack 数据如下。
10
1 2
2 3
2 4
3 5
3 6
3 7
4 8
4 9
4 10贪心的思路是对于每个节点,根据深度判断已经可以染黑多少节点,然后这一路过来一共要染黑多少节点之后判断大小。
然而,由于是涂完色再走,你完全无法确定对面会怎么走。更具体的说,两种路径有可能有一个前缀是一样的,但是染色方法是完全不同的。这就导致你完全无法判断对面要怎么走,也不知道如何染色。
















