[JOI 2026 SemiFinal] 顺流而下 / River Rafting 题解
[JOI 2026 SemiFinal] 顺流而下 / River Rafting
思路:
首先有重要观察,即每个节点一定是被它的祖先节点所点亮的,因为祖先节点的操作一定累积的比儿子节点多,所以不可能被儿子点亮,而被其他分支的节点点亮也不会优于祖先。
考虑 DP,由于要考虑到我们的节点还需多少次操作才能全部点亮,所以这样定义状态。设 \(f_{x,i,j}\) 表示 \(x\) 的子树,\(x\) 被操作了 \(i\) 次即亮度为 \(i\),子树内没有被点亮的点距离 \(x\) 的距离不超过 \(j\) 的最小代价。\(f_{x,i,0}\) 表示 \(x\) 的子树在 \(i\) 次操作后已经完全被点亮的最小代价。
转移是不难的,\(f'_{x,j+k,i} \leftarrow f_{x,j,i}+f_{v,k,i-1}\),其中 \(i-1\) 是因为对于 \(x\) 距离不超过 \(i\),则对于 \(v\) 来说距离不超过 \(i-1\)。因为 \(f\) 的定义是没被点亮的节点距离不超过 \(j\),所以要对 \(j\) 这一维取前缀最小值。
然后考虑对 \(f_{x,i,0}\) 的转移,有 \(f_{x,i,0} \leftarrow f_{x,j,i}+\max(0,i-j)\times C_{x}\),此处 \(C_x\) 是取的子树内最小值。最后答案为 \(\min_{i}f_{1,i,0}\)。有人可能会疑惑,为什么只从 \(x\) 出发。只从 \(x\) 出发是因为就算这不是最优,最优的选项也已经在递归中途被子树内节点考虑过了,每个节点只考虑从自己出发就行了,而默认操作 \(i-j\) 次也是因为我们只考虑用 \(x\) 这个节点覆盖到所有,所以 \(x\) 从当前亮度 \(j\) 到达亮度 \(i\) 就是操作 \(i-j\) 次。
现在我们已经做到时间复杂度 \(O(n^3)\),空间 \(O(n^3)\),会被卡空间。主流做法就是开动态数组,可以做到空间 \(O(n^2)\),但是这里提一个不一样的做法吧,看见状态里有一维深度维,显然可以长链剖分优化 DP,而且常数远小于动态数组做法,可以通过此题。
细节:
- 指针每次不能只加上 \(siz_x\),应该多预留一些空间。
- 就算没有长儿子,也要对 \(f_{x,i,0}\) 做转移。
代码:
vector 版:
#include<iostream>
#include<vector>
using namespace std;
#define int long long
inline int read(){
int k=0,f=1,c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())k=k*10+(c^48);
return k*f;
}
inline void write(int x){
if(x<10)putchar(x+'0');
else write(x/10),putchar(x%10+'0');
}
int n;
const int N = 7e2+10;
const int inf=0x3F3F3F3F3F3F3F3F;
vector<int>e[N];
int c[N],dep[N],fa[N],siz[N],tmp[N][N];
void dfs(int x){
dep[x]=dep[fa[x]]+1;
siz[x]=1;
for(auto v:e[x]){
dfs(v);
siz[x]+=siz[v];
c[x]=min(c[x],c[v]);
}
}
vector<vector<int> >dp(int x){
vector<vector<int> >f=vector<vector<int> >(siz[x]+5,vector<int>(siz[x]+5,inf));
vector<vector<int> >g;
for(int i=1;i<=siz[x];i++){
f[0][i]=0;
}
f[0][0]=inf;
int sz=1;
for(auto v:e[x]){
g=dp(v);
for(int i=1;i<=sz+siz[v];i++){
for(int j=0;j<=sz;j++){
for(int k=0;k<=siz[v];k++){
if(i-1>siz[v]){
if(k==0)tmp[j][i]=min(tmp[j][i],f[j][i]);
}else tmp[j+k][i]=min(tmp[j+k][i],f[j][i]+g[k][i-1]);
}
}
}
sz+=siz[v];
for(int i=0;i<=sz;i++){
for(int j=1;j<=sz;j++){
f[i][j]=tmp[i][j];
tmp[i][j]=inf;
}
}
}
for(int i=1;i<=siz[x];i++){
for(int j=0;j<=siz[x];j++){
f[i][0]=min(f[i][0],f[j][i]+max(0ll,i-j)*c[x]);
}
}
for(int i=1;i<=siz[x];i++){
for(int j=1;j<=siz[x];j++){
f[i][j]=min(f[i][j],f[i][j-1]);
}
}
return f;
}
signed main(){
n=read();
for(int i=2;i<=n;i++){
int x;
x=read();
fa[i]=x;
e[x].push_back(i);
}
for(int i=1;i<=n;i++)c[i]=read();
dfs(1);
for(int i=0;i<=n;i++){
for(int j=0;j<=n;j++){
tmp[i][j]=inf;
}
}
int ans=inf;
vector<vector<int> >f=dp(1);
for(int i=1;i<=n;i++){
ans=min(ans,f[i][0]);
}
write(ans);
return 0;
}
长剖版:
#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read(){
int k=0,f=1,c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())k=k*10+(c^48);
return k*f;
}
inline void write(int x){
if(x<10)putchar(x+'0');
else write(x/10),putchar(x%10+'0');
}
int n;
const int N = 7e2+10;
const int inf=1e17;
vector<int>e[N];
int c[N],dep[N],fa[N],siz[N],tmp[N][N],len[N],lc[N];
int p[25*N*N];
int *now=p,*f[N][N];
void dfs(int x){
dep[x]=dep[fa[x]]+1;
siz[x]=1;
for(auto v:e[x]){
dfs(v);
siz[x]+=siz[v];
c[x]=min(c[x],c[v]);
if(len[v]>len[lc[x]])lc[x]=v;
}
len[x]=len[lc[x]]+1;
}
void dp(int x){
for(int i=1;i<=siz[x];i++){
for(int j=0;j<=siz[x];j++){
f[x][i][j]=inf;
}
}
for(int i=1;i<=siz[x];i++)f[x][0][i]=0;
f[x][0][0]=inf;
int sz=1;
if(lc[x]){
for(int i=0;i<=siz[lc[x]];i++){
f[lc[x]][i]=f[x][i]+1;
}
dp(lc[x]);
sz+=siz[lc[x]];
for(auto v:e[x]){
if(v==lc[x])continue;
for(int i=0;i<=siz[v];i++){
f[v][i]=now;
now+=(siz[v]+5);
}
dp(v);
for(int i=1;i<=sz+siz[v];i++){
for(int j=0;j<=sz;j++){
for(int k=0;k<=siz[v];k++){
if(i-1>siz[v]){
if(!k)tmp[j][i]=min(tmp[j][i],f[x][j][i]);
}else{
tmp[j+k][i]=min(tmp[j+k][i],f[x][j][i]+f[v][k][i-1]);
}
}
}
}
sz+=siz[v];
for(int i=0;i<=sz;i++){
for(int j=1;j<=sz;j++){
f[x][i][j]=tmp[i][j];
tmp[i][j]=inf;
}
}
}
}
for(int i=1;i<=siz[x];i++){
for(int j=0;j<=siz[x];j++){
f[x][i][0]=min(f[x][i][0],f[x][j][i]+max(0ll,i-j)*c[x]);
}
}
for(int i=1;i<=siz[x];i++){
for(int j=1;j<=siz[x];j++){
f[x][i][j]=min(f[x][i][j],f[x][i][j-1]);
}
}
}
signed main(){
n=read();
for(int i=2;i<=n;i++){
int x;
x=read();
fa[i]=x;
e[x].push_back(i);
}
for(int i=1;i<=n;i++)c[i]=read();
dfs(1);
for(int i=0;i<=n;i++){
for(int j=0;j<=n;j++){
tmp[i][j]=inf;
}
}
for(int i=0;i<=n;i++){
f[1][i]=now;
now+=(n+5);
}
dp(1);
int ans=inf;
for(int i=1;i<=n;i++){
ans=min(ans,f[1][i][0]);
}
write(ans);
return 0;
}

浙公网安备 33010602011771号