P9021 [USACO23JAN] Subtree Activation P 题解

P9021 [USACO23JAN] Subtree Activation P 题解


知识点

树形 DP。

分析

转化

首先我们可以想到直接在树上进行移动覆盖,最优答案就是覆盖所有节点的最小花费。

我们设初始状态为 \(0\) 节点,从 \(0\) 节点移动到任意节点 \(u(u>0)\) 花费都是 \(siz_u\)

当我们现在的状态满足都为 \(u(u>0)\) 的子树,我们有以下三种选择:

  1. 移动到 \(0\) 节点,花费 \(siz_u\)
  2. 移动到父节点 \(fa_u\),花费 \(siz_{fa_u}-siz_u\)
  3. 移动到子节点 \(v\),花费 \(siz_u-siz_v\)

我们发现,最优的方式就是在树上一条简单路径覆盖过去,直到每个点都被覆盖过,列式发现这样可以避免一些重复。那么每次花费为 \(2\max_{u}{siz_u}\),其中 \(u\) 是路径上的点。

问题转化为树上最小路径覆盖问题

DP

此时我们就可以来做树形 DP 了:

  • \(f_{u,0}\) 表示当 \(u\) 的子树全部被覆盖过了的最小花费。
  • \(f_{u,1}\) 表示当 \(u\) 的子树中除了 \(u\) 到某个叶节点的路径其余全部被覆盖过的最小花费。

那么有转移:

  1. \(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) \\ \]

  2. \(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;
}

posted @ 2025-05-12 22:12  Add_Catalyst  阅读(9)  评论(0)    收藏  举报