[题解]AT_abc223_g [ABC223G] Vertex Deletion
思路
显然我们需要求出原树的最大匹配。
定义 表示在 为根的子树中进行匹配,且 不选/选 的最大匹配。状态转移方程比较显然:
其中 ,然后 是使得 最大的一个 。显然这一步是可以 计算的。
注意到,我们需要计算删除 后的最大匹配,这等同于计算以 为根时的 。于是考虑换根 DP。
令当前的根由 转为 。接下来只需简单分讨,记转移 时的 为 , 为 。
由于 由 的儿子变为了父亲, 的值一定发生变化。显然 。其次:
-
当 时:,然后和其次大值进行匹配。因此还需在第一次 DFS 时处理出次大的儿子。
-
否则: 直接减去 即可。
然后可以更新 。接下来更新 。
显然的是 都需要加上 。
其次,如果 小于 带来的贡献,那么更新掉即可。需要注意的是, 以及维护的次大值都需要被更新。
这样就完成了所有元素的更新,继续向下换根即可。
Code
#include <bits/stdc++.h>
#define re register
using namespace std;
const int N = 2e5 + 10,M = 4e5 + 10,inf = 1e9 + 10;
int n,ans;
int idx,h[N],ne[M],e[M];
int res,dp[N][2],val[N],Max[2][N],id[2][N];
// 这里 Max[0/1] 和 id[0/1] 分别维护的是最小值和次小值
inline int read(){
int r = 0,w = 1;
char c = getchar();
while (c < '0' || c > '9'){
if (c == '-') w = -1;
c = getchar();
}
while (c >= '0' && c <= '9'){
r = (r << 3) + (r << 1) + (c ^ 48);
c = getchar();
}
return r * w;
}
inline void add(int a,int b){
ne[idx] = h[a];
e[idx] = b;
h[a] = idx++;
}
inline void dfs1(int u,int fa){
int mMax = -inf,fMax = -inf;
int mid = 0,fid = 0;
val[u] = dp[u][0] = dp[u][1] = 0;
for (re int i = h[u];~i;i = ne[i]){
int j = e[i];
if (j == fa) continue;
dfs1(j,u);
dp[u][0] += val[j];
dp[u][1] += val[j];
int t = dp[j][0] - val[j] + 1;
if (mMax < t){
fMax = mMax; fid = mid;
mMax = t; mid = j;
}
else if (fMax < t){
fMax = t; fid = j;
}
}
Max[0][u] = mMax; Max[1][u] = fMax;
id[0][u] = mid; id[1][u] = fid;
dp[u][1] += max(0,mMax);
val[u] = max(dp[u][0],dp[u][1]);
}
inline void dfs2(int u,int fa){
ans += (dp[u][0] == res);
for (re int i = h[u];~i;i = ne[i]){
int j = e[i];
if (j == fa) continue;
int dpu0 = dp[u][0],dpu1 = dp[u][1],valu = val[u];
int Max0u = Max[0][u],Max1u = Max[1][u],id0u = id[0][u],id1u = id[1][u];
int dpj0 = dp[j][0],dpj1 = dp[j][1],valj = val[j];
int Max0j = Max[0][j],Max1j = Max[1][j],id0j = id[0][j],id1j = id[1][j];
dp[u][0] -= val[j];
if (id[0][u] == j){
dp[u][1] -= (dp[j][0] + 1);
if (id[1][u]) dp[u][1] += Max[1][u];
}
else dp[u][1] -= val[j];
val[u] = max(dp[u][0],dp[u][1]);
dp[j][0] += val[u]; dp[j][1] += val[u];
int t = dp[u][0] - val[u] + 1;
if (Max[0][j] < t){
dp[j][1] -= Max[0][j];
Max[1][j] = Max[0][j]; id[1][j] = id[0][j];
Max[0][j] = t; id[0][j] = u;
dp[j][1] += Max[0][j];
}
else if (Max[1][j] < t){
Max[1][j] = t; id[1][j] = u;
}
dfs2(j,u);
dp[u][0] = dpu0; dp[u][1] = dpu1; val[u] = valu;
Max[0][u] = Max0u; Max[1][u] = Max1u; id[0][u] = id0u; id[1][u] = id1u;
dp[j][0] = dpj0; dp[j][1] = dpj1; val[j] = valj;
Max[0][j] = Max0j; Max[1][j] = Max1j; id[0][j] = id0j; id[1][j] = id1j;
}
}
int main(){
memset(h,-1,sizeof(h));
n = read();
for (re int i = 1;i < n;i++){
int a,b; a = read(),b = read();
add(a,b); add(b,a);
}
dfs1(1,0); res = val[1];
dfs2(1,0);
printf("%d",ans);
return 0;
}
作者:WaterSun
出处:https://www.cnblogs.com/WaterSun/p/18261951
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】