Luogu P3155 [CQOI2009] 叶子的染色 [ 蓝 ] [ 树形 DP ]
谁家绿题这么难啊?
结论 \(1\):对于任意非叶子节点 \(u, v\),以 \(u\) 为根求得的最优解与以 \(v\) 为根求得的最优解相同。即根的选择不影响答案。
证明:
考虑调整法,假设我要从 \(u\) 换根到它的一个儿子 \(v\),进行分类讨论:
- 当根节点 \(u\) 有色,儿子 \(v\) 无色。我们把 \(\bm v\) 染上 \(\bm u\) 的颜色,然后使 \(\bm u\) 变为无色,构造依然成立,而染色个数不变。
- 当根节点 \(u\) 有色,儿子 \(v\) 有色。此时 \(u, v\) 的作用范围都不变。我们无需做任何改变,构造依然成立。
- 当根节点 \(u\) 无色,儿子 \(v\) 无色。显然没有影响。我们无需做任何改变,构造依然成立。
- 当根节点 \(u\) 无色,儿子 \(v\) 有色。此时 \(v\) 子树内部受到的影响不变,而子树外部因为原本就已经保证了没有未染色的节点,故 \(v\) 对子树外部也无法造成影响。我们无需做任何改变,构造依然成立。
原命题得证。
因此随便钦定一个根,做树形 DP:定义 \(dp_{u, 0/1/2}\) 表示该节点子树内有待染为白色 / 待染为黑色 / 不需要染色的最小操作数。转移如下:
- \(dp_{u, 0}\overset{+}\leftarrow \min\{dp_{v, 0}, dp_{v, 2}\}\)。
- \(dp_{u, 1}\overset{+}\leftarrow \min\{dp_{v, 1}, dp_{v, 2}\}\)。
- \(dp_{u, 2}\overset{}\leftarrow \min\{\sum dp_{v, 2}, 1+\sum \min\{dp_{v, 0}, dp_{v, 2} \}, 1+\sum \min\{dp_{v, 1}, dp_{v, 2} \}\}\)。
时间复杂度 \(O(n)\)。
#include <bits/stdc++.h>
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc(x) (tr[x].ls)
#define rc(x) (tr[x].rs)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi = pair<int, int>;
const int N = 10005, inf = 1e5;
int n, m, c[N];
vector<int> g[N];
int dp[N][3];
void dfs(int u, int fa)
{
if(u <= m)
{
dp[u][c[u]] = 0;
dp[u][c[u] ^ 1] = inf;
dp[u][2] = 1;
return;
}
int sm1 = 0, sm0 = 0;
for(auto v : g[u])
{
if(v == fa) continue;
dfs(v, u);
dp[u][0] += min(dp[v][0], dp[v][2]);
dp[u][1] += min(dp[v][1], dp[v][2]);
dp[u][2] += dp[v][2];
sm1 += min(dp[v][1], dp[v][2]);
sm0 += min(dp[v][0], dp[v][2]);
}
dp[u][2] = min(dp[u][2], min(sm1 + 1, sm0 + 1));
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
for(int i = 1; i <= m; i++) cin >> c[i];
for(int i = 1; i < n; i++)
{
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
dfs(m + 1, 0);
cout << dp[m + 1][2];
return 0;
}

浙公网安备 33010602011771号