P9021 [USACO23JAN] Subtree Activation P 题解
P9021 [USACO23JAN] Subtree Activation P 题解
知识点
树形 DP。
分析
转化
首先我们可以想到直接在树上进行移动覆盖,最优答案就是覆盖所有节点的最小花费。
我们设初始状态为 \(0\) 节点,从 \(0\) 节点移动到任意节点 \(u(u>0)\) 花费都是 \(siz_u\)。
当我们现在的状态满足都为 \(u(u>0)\) 的子树,我们有以下三种选择:
- 移动到 \(0\) 节点,花费 \(siz_u\)。
- 移动到父节点 \(fa_u\),花费 \(siz_{fa_u}-siz_u\)。
- 移动到子节点 \(v\),花费 \(siz_u-siz_v\)。
我们发现,最优的方式就是在树上一条简单路径覆盖过去,直到每个点都被覆盖过,列式发现这样可以避免一些重复。那么每次花费为 \(2\max_{u}{siz_u}\),其中 \(u\) 是路径上的点。
问题转化为树上最小路径覆盖问题。
DP
此时我们就可以来做树形 DP 了:
- 设 \(f_{u,0}\) 表示当 \(u\) 的子树全部被覆盖过了的最小花费。
- 设 \(f_{u,1}\) 表示当 \(u\) 的子树中除了 \(u\) 到某个叶节点的路径其余全部被覆盖过的最小花费。
那么有转移:
-
\(f_{u,0}\) 的转移有两种,取其中较小值:
-
子树中选一条经过 \(u\) 的链,把它覆盖了,其余都是已经覆盖过的:
\[\sum_{v\in Son_{u}} f_{v,0} + \min_{x\in Son_{u}} (f_{x,1}-f_{x,0}) + \min_{y\in Son_{u},x\neq y} (f_{y,1}-f_{y,0}) + 2siz_u \\ \] -
找一条经过子节点 \(v\) 的链,把它延长到 \(u\):(这样可能会把它弄得不是链,但是不影响最终的答案)
\[\sum_{v\in Son_{u}} f_{v,0} + 2(siz_u - \max_{v\in Son_u}siz_v) \\ \]
-
-
\(f_{u,1}\) 的转移十分简单,直接从子节点连接过来即可:
\[\min_{x\in Son_u} f_{x,1} + \sum_{y\in Son_{u},x\neq y} f_{y,0} \\ \]
复杂度 \(O(n)\)。
//#define Plus_Cat ""
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
#define RCL(a,b,c,d) memset(a,b,sizeof(c)*(d))
#define FOR(i,a,b) for(int i(a);i<=(int)(b);++i)
#define DOR(i,a,b) for(int i(a);i>=(int)(b);--i)
#define tomax(a,...) ((a)=max({(a),__VA_ARGS__}))
#define tomin(a,...) ((a)=min({(a),__VA_ARGS__}))
#define EDGE(g,i,x,y) for(int i=(g).h[(x)],y=(g)[(i)].v;~i;y=(g)[(i=(g)[i].nxt)>0?i:0].v)
#define main Main();signed main(){ios::sync_with_stdio(0);cin.tie(0),cout.tie(0);return Main();}signed Main
using namespace std;
constexpr int N(2e5+10);
namespace IOEcat {
//...
} using namespace IOEcat;
int n,ans(INF);
int siz[N];
int f[N][2];
vector<int> g[N];
void dfs(int u,int fa) {
siz[u]=1;
int sum(0),mn(0),mn2(0),mx(-INF);
for(const int &v:g[u]) {
dfs(v,u),siz[u]+=siz[v],tomax(mx,siz[v]),sum+=f[v][0];
if(f[v][1]-f[v][0]<mn)mn2=mn,mn=f[v][1]-f[v][0];
else tomin(mn2,f[v][1]-f[v][0]);
}
if(siz[u]==1)return f[u][0]=2,void();
f[u][0]=min((siz[u]<<1)+sum+mn+mn2,sum+((siz[u]-mx)<<1)),f[u][1]=sum+mn;
}
signed main() {
#ifdef Plus_Cat
freopen(Plus_Cat ".in","r",stdin),freopen(Plus_Cat ".out","w",stdout);
#endif
I(n);
FOR(i,2,n) {
int fa;
I(fa),g[fa].push_back(i);
}
dfs(1,0),O(f[1][0],'\n');
return 0;
}

浙公网安备 33010602011771号