P9864 [POI 2021/2022 R2] age 题解

P9864 [POI 2021/2022 R2] age 题解


知识点

树形最值 DP,分类讨论。

分析

简化题意

给定 \(k\) 个关键点,将树划分为 \(k\) 个连通块,每个块包含一个关键点。

设特殊点 \(i\) 在其联通块内为端点的最长链长度为 \(len_i\),则一种划分方案的代价为:\(2(n-k)-\sum_{i=1}^klen_i\)

问所有划分方案中的最小代价。

条件挖掘

发现代价为 \(2(n-k)-\sum_{i=1}^klen_i\),那么我们求 \(\sum_{i=1}^k len_i\) 的最大值即可。

首先对于每个连通块,我们发现除最长链以外的所有点都是无用的,保留的同时还会影响转移,删去又不会对答案造成影响,所以对于每个连通块我们只需记一条最长链即可。

DP 设计

我们直接考虑链长什么样,在树上的链无非就两种:一端为 LCA 或两端都不为 LCA。再加上这个链一端必须为关键点,这个时候就有很多很好用的性质。

  1. 一端为 LCA:

    我们考虑两端点哪个是关键点。

    • 深度大的:

      我们可以设一个表示从关键点延伸出来,还没有结尾时的状态。

    • 深度小的:

      我们可以设一个表示从非关键点延伸出来,还没有结尾时的状态。

  2. 两端都不为 LCA:

    此时这条链就相当于上面「一端为 LCA」中「深度大的」和「深度小的」两个状态并起来,也很好解决,只不过这个状态时就不能再像上面两者一样继续延伸了,所以要另设一个状态记。

那么综上,就有 \(f_{u,0/1/2}\) 表示转移到 \(u\) 点,「不能再延伸」或「从关键点延伸出来」或「从非关键点延伸出来」时的 \(\sum_{i=1}^k len_i\) 的最大值。

初态 & 转移

假设现在转移到了点 \(u\),设 \(sum=\sum_{v\in trans(u)} f_{v,0}\)

\(u\)​ 为关键点

  • \(f_{u,0}\)

    初始值为 \(sum\),表示没有从下往上延伸的连接它。那么与之对应的就是有从下往上延伸的连接它,且它是端点:

    \[\max_{v\in trans(u)} \set{sum - f_{v,0} + f_{v,2} + 1} \\ \]

  • \(f_{u,1}\):就为 \(sum\),表示从自己出发到自己。

  • \(f_{u,2}\):不合法,在代码中设为 \(-\inf\)

\(u\) 不为关键点

  • \(f_{u,0}\)

    初始值还是为 \(sum\),表示没有从下往上延伸的连接它。同样与之对应,表它是端点,不过稍有改动:

    \[\max_{v\in trans(u)} \set{sum - f_{v,0} + f_{v,1} + 1} \\ \]

    此时还要连接出一个两端都不为 LCA 的链,且它是 LCA:

    \[\max_{v_0\in trans(u)} \set{sum - f_{v_0,0} + f_{v_0,1} + 1 + \max_{v\neq v_0} \set{- f_{v,0} + f_{v,2} + 1}} \]

    那么分类讨论枚举特判一下即可解决。

  • \(f_{u,1}\)

    初始值为 \(-\inf\),因为没有合法的状态。然后转移也是类似:

    \[\max_{v\in trans(u)} \set{sum - f_{v,0} + f_{v,1} + 1} \\ \]

  • \(f_{u,2}\)

    初始值为 \(sum\),表示它是一个端点。转移:

    \[\max_{v\in trans(u)} \set{sum - f_{v,0} + f_{v,2} + 1} \\ \]

代码

//#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(5e5+10);

bool mark[N];
int n,m,rt;
int f[N][3];
vector<int> g[N];

void DP(int u,int fa) {
	int sum(0);
	for(const int &v:g[u])if(v^fa)DP(v,u),sum+=f[v][0];
	if(mark[u]) {
		//0
		f[u][0]=sum;
		for(const int &v:g[u])if(v^fa)tomax(f[u][0],sum-f[v][0]+f[v][2]+1);
		//1
		f[u][1]=sum;
		//2
		f[u][2]=-INF;
	} else {
		//0
		f[u][0]=sum;
		for(const int &v:g[u])if(v^fa)tomax(f[u][0],sum-f[v][0]+f[v][1]+1);
		int mx(-INF),smx(-INF);
		for(const int &v:g[u])if(v^fa)
			f[v][2]-f[v][0]+1>mx?smx=mx,mx=f[v][2]-f[v][0]+1:tomax(smx,f[v][2]-f[v][0]+1);
		for(const int &v:g[u])if(v^fa)
			tomax(f[u][0],sum+(f[v][2]-f[v][0]+1==mx?smx:mx)-f[v][0]+f[v][1]+1);
		//1
		f[u][1]=-INF;
		for(const int &v:g[u])if(v^fa)tomax(f[u][1],sum-f[v][0]+f[v][1]+1);
		//2
		f[u][2]=sum;
		for(const int &v:g[u])if(v^fa)tomax(f[u][2],sum-f[v][0]+f[v][2]+1);
	}
}

signed main() {
#ifdef Plus_Cat
	freopen(Plus_Cat ".in","r",stdin),freopen(Plus_Cat ".out","w",stdout);
#endif
	cin>>n>>m;
	FOR(i,1,m)cin>>rt,mark[rt]=true;
	FOR(i,2,n) {
		int u,v;
		cin>>u>>v,g[u].push_back(v),g[v].push_back(u);
	}
	DP(rt,0),cout<<2*(n-m)-max(f[rt][0],f[rt][1])<<endl;
	return 0;
}

posted @ 2025-06-19 21:36  Add_Catalyst  阅读(41)  评论(0)    收藏  举报