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。再加上这个链一端必须为关键点,这个时候就有很多很好用的性质。
-
一端为 LCA:
我们考虑两端点哪个是关键点。
-
深度大的:
我们可以设一个表示从关键点延伸出来,还没有结尾时的状态。
-
深度小的:
我们可以设一个表示从非关键点延伸出来,还没有结尾时的状态。
-
-
两端都不为 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;
}

浙公网安备 33010602011771号