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;
}
posted @ 2025-11-20 14:37  KS_Fszha  阅读(3)  评论(0)    收藏  举报