题解:P14637 [NOIP2025] 树的价值 / tree(官方数据)
纪念第一个全调完的 CCF 系列赛事。
第一眼看过去,发现明显部分分分成三部分,考虑第一部分
我们考虑到一个节点为根的子树中所有节点
那么这个子树的
考虑一个假贪心就是每次一个节点填的数使得这个子树恰好相对最大的儿子垫高一。
不过我们并不认为它很有道理,而且注意到数据范围,所以不对。
我们考虑如果不是这样,那么这个子树就会和最大的那个儿子的
考虑可以这里填的大一些,在某个祖先处使得祖先变大
于是设
转移就是枚举
这样实现优秀就是
但是这样细节不是特别少,而且分数不高,而且题目给的
于是考虑刻画这个东西。
我们发现根在使用没用的节点之前,
我们考虑一个没用的节点在当前使用会发生什么。
此时我们发现从这里往上一直走继承边的节点都会被贡献,但如果某次走的不是继承边,之后不被贡献。
我们到这里发现可以贪心一部分,就是一个节点只给他到根节点中最长的连续嫡长子的底部进行贡献,产生链长的贡献。
我们考虑 DP 过程钦定嫡长子来计算答案。
设
发现非常好转移,还是枚举
复杂度
获得
考虑怎么优化,其实我们第二维可以去掉,然后设当前节点为当前嫡长子连最顶端,枚举当前链到哪个叶子暴力转移。
考虑稍微聪明点,设
然后我们考虑
但是
复杂度还是假的。
设
于是每次根往上走就是对每个
复杂度就是子树大小之和乘
#include<bits/stdc++.h>
#define int long long
using namespace std;
int dp[8009][809];
int f[8009][809];
vector<int> s[8009];
int dep[8009];
int ped[8009];
int sz[8009];
int pd[8009][809];
int fa[8009];
int dfn[8009];//point i's bianhao
int rr[8009];//bianhao's r
int inn;
struct szsz{
int f[8009];
int lowbit(int t){
return t&(-t);
}
void op(int t,int k){
while(t<=8001){
f[t]+=k;
t+=lowbit(t);
}
}
int query(int t){
int ans;
ans=0;
while(t){
ans+=f[t];
t-=lowbit(t);
}
return ans;
}
void pls(int l,int r,int k){
op(l,k),op(r+1,-k);
}
}w[809];
void fs(int t){
int cc;
cc=inn+1;
dfn[t]=++inn;
for(auto v:s[t]){
fs(v);
}
rr[cc]=inn;
}
void zy(int t,int rt){
if(dep[t]-dep[rt]+1>dep[t]){
return;
}
for(auto v:s[t]){
zy(v,rt);
}
dp[rt][dep[t]-dep[rt]+1]=max(dp[rt][dep[t]-dep[rt]+1],w[dep[t]-dep[rt]+1].query(dfn[t])+(dep[t]-dep[rt])*(dep[t]-dep[rt]+1));
}
void dfs(int t){
ped[t]=1;
sz[t]=1;
for(auto v:s[t]){
dfs(v);
ped[t]=max(ped[t],ped[v]+1);
sz[t]+=sz[v];
}for(int ma=1;ma<=dep[t]+1;ma++){
for(auto v:s[t]){
pd[t][ma]+=dp[v][ma];
}
}
for(int ma=1;ma<=dep[t];ma++){
int sum;
sum=0;
for(auto v:s[t])
sum+=dp[v][ma];
for(auto v:s[t])
f[t][ma]=max(f[t][ma],sum-dp[v][ma]+f[v][ma+1]);
f[t][ma]+=ma;
w[ma].pls(dfn[t],dfn[t],f[t][ma]);
}
dp[t][1]=f[t][1];
for(int i=1;i<=dep[t]+1;i++){
int cc=0;
for(auto v:s[t]){
cc+=dp[v][i];
}
for(auto v:s[t]){
cc-=dp[v][i];
w[i].pls(dfn[v],rr[dfn[v]],cc);
cc+=dp[v][i];
}
}
zy(t,t);
for(int ma=ped[t];ma<=dep[t];ma++){
dp[t][ma]=sz[t]*ma;
}
}
void _main(){
int n,m;
cin>>n>>m;
inn=0;
for(int i=1;i<=n;i++){
s[i].clear();
}
memset(dp,0,sizeof(dp));
memset(pd,0,sizeof(pd));
memset(f,0,sizeof(f));
memset(w,0,sizeof(w));
dep[1]=1;
for(int i=2;i<=n;i++){
int faa;
cin>>faa;
dep[i]=dep[faa]+1;
fa[i]=faa;
s[faa].push_back(i);
}
fs(1);
dfs(1);
cout<<dp[1][1]<<endl;
}
signed main(){
//freopen("tree.in","r",stdin);
//freopen("tree.out","w",stdout);
std::ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;
cin>>t;
while(t--){
_main();
}
return 0;
}