专栏文章
题解:P11529 [THUPC2025 初赛] 辞甲猾扎
P11529题解参与者 3已保存评论 2
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 2 条
- 当前快照
- 1 份
- 快照标识符
- @miqjpyo5
- 此快照首次捕获于
- 2025/12/04 05:55 3 个月前
- 此快照最后确认于
- 2025/12/04 05:55 3 个月前
稍微转换一下题目,由于所有灰点都不被变成黑色,所以等价于与黑色相邻的点最后都是白色(相邻都被变成后白色黑色就“扩散”不出去了),也就是其要么本身为白色,要么其还和一个白点相邻。
原图是一棵树,考虑树形 dp。稍微分讨一下可以得出一下定义:
- 表示 是灰点,不需要与白色相邻,其子树内最少要染白的个数
- 表示 是灰点,需要与白色相邻且已与白色相邻,其子树内最少要染白的个数
- 表示 是灰点,需要与白色相邻且未与白色相邻,其子树内最少要染白的个数
- 表示 是白点,其子树内最少要染白的个数
- 表示 是黑点,其子树内最少要染白的个数
设当前有 向 转移(),分三种情况:
- 是黑点:
- ( 不存在, 的 不满足条件)
- 是灰点且未与黑点相邻:
- ( 不存在, 的 不满足条件)
- (除了 不存在其它随便转)
- 是灰点且与黑点相邻:
- ( 的 不满足条件, 的 矛盾)
- (这个真没任何限制了)
- 先对于每个子树求随便染()的方案数和,然后选其中染白代价()最小的一个换成
答案即为 。
感觉做得有些复杂了,不过状态定义清楚了就很好做了 (但不好说是谁想状态想了 2h 吃了三发罚时)。所以为什么这题过得比 I 和 D 还多啊。
CPP#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6 + 5;
const int INF = 1e9;
bool vis[N], f1[N];
int f[N][5];
vector <int> p[N];
void dfs(int k, int fa) {
f[k][3] = 1;
for (auto i : p[k])
if (i != fa) dfs(i, k);
if (vis[k]) {
f[k][0] = f[k][1] = f[k][2] = f[k][3] = INF;
for (auto i : p[k])
if (i != fa)
f[k][4] += min({f[i][1], f[i][3], f[i][4]});
} else if (!f1[k]) {
f[k][1] = f[k][2] = f[k][4] = INF;
for (auto i : p[k]) {
if (i == fa) continue;
f[k][0] += min({f[i][0], f[i][1], f[i][3]});
f[k][3] += min({f[i][0], f[i][1], f[i][2], f[i][3]});
}
} else {
f[k][0] = f[k][4] = INF; int t = INF;
for (auto i : p[k]) {
if (i == fa) continue;
f[k][2] += min({f[i][0], f[i][1], f[i][4]});
f[k][3] += min({f[i][0], f[i][1], f[i][2], f[i][3], f[i][4]});
int w = min({f[i][0], f[i][1], f[i][3], f[i][4]});
if (!vis[i]) t = min(t, f[i][3] - w); f[k][1] += w;
}
f[k][1] += t;
}
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int n, k; cin >> n >> k;
for (int i = 1, x; i <= k; i++)
cin >> x, vis[x] = true;
for (int i = 1, u, v; i < n; i++) {
cin >> u >> v;
p[u].push_back(v);
p[v].push_back(u);
f1[u] |= vis[v], f1[v] |= vis[u];
}
dfs(1, 0); cout << min({f[1][0], f[1][1], f[1][3], f[1][4]});
return 0;
}
相关推荐
评论
共 2 条评论,欢迎与作者交流。
正在加载评论...