[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,而且常数远小于动态数组做法,可以通过此题。

细节:

  1. 指针每次不能只加上 \(siz_x\),应该多预留一些空间。
  2. 就算没有长儿子,也要对 \(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;
}
posted @ 2026-08-15 21:20  farenara_sayowell  阅读(30)  评论(0)    收藏  举报