题解:P12382 [蓝桥杯 2023 省 Python B] 树上选点
前言
很不寻常的树形 dp,调了我两小时。
思路
先看第二点要求:任意两点
意思就是说选的点的深度必须互不相同,即树的每一层最多只能选
再看第一点要求:任意两点
显然层是状态,我们应当从叶子开始,逐步递推到根。
我设计
接下来考虑转移,比较简单。如果我们想选第
如果把第
这可以用前后缀最大值来解决。
无论是 dfs 预处理,还是 dp 过程,每个点都只会被处理常数次,所以时间复杂度为
代码
#include<bits/stdc++.h>
using namespace std;
int n;
vector<int>dp[200003];//[点的深度][深度为i的点中选的哪个点]
vector<int>edg[200003];
vector<int>rid[200003];//第i层第j个点的原始编号是多少
int v[200003];
int id[200003],idl[200003];
//在深度为i的点中的编号,深度为i的点的数量
int lr[200003][2];
int depmax;
void dfs(int p,int dep){
id[p]=++idl[dep];
if(rid[dep].size()==0)rid[dep].push_back(0);
rid[dep].push_back(p);
for(auto i:edg[p]){
dfs(i,dep+1);
if(!lr[p][0])lr[p][0]=id[i];
lr[p][1]=id[i];
}
depmax=max(depmax,dep);
}
int mx[2][200003];
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n;
for(int i=2;i<=n;i++){
int f;cin>>f;
edg[f].push_back(i);
}
for(int i=1;i<=n;i++)cin>>v[i];
dfs(1,1);
for(int i=0;i<=depmax;i++){
for(int j=0;j<=idl[i];j++)dp[i].push_back(0);
}
for(int i=0;i<=idl[depmax];i++){
dp[depmax][i]=v[rid[depmax][i]];
}
for(int i=depmax-1;i>0;i--){
for(int j=1;j<=idl[i+1];j++)mx[0][j]=mx[1][j]=0;
mx[0][1]=dp[i+1][1];
for(int j=2;j<=idl[i+1];j++)mx[0][j]=max(mx[0][j-1],dp[i+1][j]);//前缀最大值
mx[1][idl[i+1]]=dp[i+1][idl[i+1]];
for(int j=idl[i+1]-1;j>=0;j--)mx[1][j]=max(mx[1][j+1],dp[i+1][j]);//后缀最大值
dp[i][0]=max(mx[1][1],dp[i+1][0]);
for(int j=1;j<=idl[i];j++){
int add=0;
if(lr[rid[i][j]][0]>1)add=max(add,mx[0][lr[rid[i][j]][0]-1]);
if(lr[rid[i][j]][1]<idl[i+1])add=max(add,mx[1][lr[rid[i][j]][1]+1]);
dp[i][j]=v[rid[i][j]]+max(add,dp[i+1][0]);
}
}
cout<<max(dp[1][1],dp[1][0]);
return 0;
}