P11529 [THUPC 2025 初赛] 辞甲猾扎

· · 题解

前言

没场切的题。

THUPC 2024 对队伍的贡献为 0 !!!

思路

看完题面,可以想到保安站岗这道题,但我们照样设计状态有点问题,对于本题,有一些点是不与黑点相邻的,他们的邻点可以没有白色,注意,是可以,不是一定邻点没有白色。

设计状态:设 dp_{0/1/2/3} 为自己染白/需要儿子染白/需要父亲染白/不需要儿子也不需要父亲染白。

转移方程:

自己染白了儿子可以随便选。 $$dp_{x,1} = \min\{dp_{x,1} + \min\{dp_{i,0},dp_{i,1},dp_{i,3}\},dp_{x,3} + dp_{i,0}\}$$。 如果自己还没有儿子染白,那么需要儿子染白,否则儿子只需要不用自己染白,其他随便选。 $$dp_{x,2} = dp_{x,2} + \min\{dp_{i,1},dp_{i,3}\}$$。 只有儿子不需要自己的帮助时,自己可以求助父亲的父亲。如果儿子染白,但自己仍求助父亲,那一定是不优于 $dp_{x,1}$ 的。 $$dp_{x,3} = dp_{x,3} + \min\{dp_{i,0},dp_{i,1},dp_{i,3}\}$$。 只需要儿子不求助自己。 注意,$dp_{x,0/1/2}$ 需要当前节点不为黑,$dp_{x,3}$ 需要对所有节点转移。 如果当前节点为灰点且与黑点相邻,那么他的邻点需要白色,那么 $dp_{x,3} = \inf$。 初始化:如果当前节点为黑,那么 $dp_{x,0/1/2} = \inf$。否则,$dp_{x,0} = 1,dp_{x,1} = \inf$。 ## 代码 ```cpp #include<bits/stdc++.h> using namespace std; #define int long long int read() { char c; int num = 0,f = 1; c = getchar(); while(c < '0' || '9' < c) { if(c == '-') { f = -1; } c = getchar(); } while('0' <= c && c <= '9') { num = num * 10 + (c - '0'); c = getchar(); } return num * f; } bool f[1000005],ff[1000005]; vector<int> v[1000005]; int dp[1000005][4]; void dfs(int x,int fa) { if(f[x]) { dp[x][0] = 2e9; dp[x][1] = 2e9; dp[x][2] = 2e9; } else { dp[x][0] = 1; dp[x][1] = 2e9; } for(auto i : v[x]) { if(i == fa) { continue; } dfs(i,x); if(!f[x]) { dp[x][0] += min({dp[i][0],dp[i][1],dp[i][2],dp[i][3]}); dp[x][1] = min(dp[x][3] + dp[i][0],dp[x][1] + min({dp[i][0],dp[i][1],dp[i][3]})); dp[x][2] += min({dp[i][1],dp[i][3]}); } dp[x][3] += min({dp[i][0],dp[i][1],dp[i][3]}); } if(!f[x] && ff[x]) { dp[x][3] = 2e9; } } signed main() { int n,k; n = read(),k = read(); for(int i = 1;i <= k;i++) { int x; x = read(); f[x] = 1; } for(int i = 1;i < n;i++) { int x,y; x = read(),y = read(); v[x].push_back(y); v[y].push_back(x); } for(int i = 1;i <= n;i++) { if(f[i]) { for(auto j : v[i]) { ff[j] = 1; } } } dfs(1,0); cout << min({dp[1][0],dp[1][1],dp[1][3]}); return 0; } ```