埋土 题解
设 \(dp(p,a,b)\) 表示 \(p\) 的子树中与它的父亲 \(f\) 联通的点的个数是 \(a\) , \(p\) 的上面与 \(f\) 联通的点的个数是 \(b\) 的最小损失,显然 \(a \ge 0,b \ge 1\)。
当 \(p\) 没有儿子时:
\(dp(p,1,i)=iv,dp(p,0,i)=v^2\),其中 \(v\) 是 \(p\) 与 \(p\) 的父亲的边的边权。
当 \(p\) 的儿子只有一个 \(s\) 时:
如果 \(p\) 与 \(p\) 的父亲不断开,显然 \(dp(p,i,j)=\min(dp(s,i-1,j+1)+i \times j \times v)\) 。
如果 \(p\) 与 \(p\) 的父亲断开,\(dp(p,0,j)=\min(dp(s,i,1)+v^2)\) 。
当 \(p\) 的儿子有很多个时:
发现如果 \(p\) 与 \(p\) 的父亲不断开,式子是 \(dp(p,i,j)=\min(\sum\limits_{s \in son_p}dp(s,k_s,l_s)+i \times j \times v)\),要求 \(\sum\limits_{s \in son_p}k_s+1=i,\sum\limits_{s \in son_p}k_s-k_t+j+1=l_t\) 。
枚举所有 \(k_s\) 和 \(j\) 并求出所有 \(l_s\) 和 \(i\) 显然复杂度爆炸。
可以再建一个子 dp \(f(s,a,b)\) 表示进行到 \(s\) ,\(\large{\sum\limits_{t=s_{first}}^sk_t=a,\sum\limits_{t=s_{next}}^{s_{last}}k_t+j+1=b}\),\(p\) 的上面有 \(j\) 个与它联通的点的 \(\large{\min(\sum\limits_{t\in son_p}^sdp(t,k_t,l_t))}\)。
于是 \(f(s_{next},i+k,j-k)=\min(f(s,i,j)+dp(s_{next},k,i+j-k))\)。
前面那一维可以滚掉,卡一下边界,可以发现 \(i\) 与 \(k\) 的大小与树上背包的转移一致,再多枚举了一个 j,所以复杂度是 \(O(n^2 \times (<n))=O(<n^3)\)。
于是如果 \(p\) 与 \(p\) 的父亲不断开, \(dp(p,i,j)=\min(f(s_{last},i-1,j+1)+i \times j \times v)\) 。
如果 \(p\) 与 \(p\) 的父亲断开,\(dp(p,0,j)=\min(f(s_{last},i,1)+v^2)\) 。
这和只有一个儿子时的转移一模一样。
但是单纯这样不能过,\(500^3\) 个 long long 的内存高达 1G,所以还要优化内存。
发现下面的 dp 处理完了之后就没用了,内存可以转移到上面,重链剖分一下,可以每条链共用一个 \(n^2\) 的内存,下面的处理完了继承到上面,在没处理完 f 时 dp 不能计算,所以可以把 f 用 dp 表示,这样最多用一个节点到根结点的链数个 \(n^2\) dp,内存是 \(O(n^2logn)\)
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define long long long
int n,lt[510][510],sz[510],cs[510];
long dp[12][510][510],f[510][510],lv[510],res;
void dfs(int p){
sz[p]=1;
for(int i=0;i<cs[p];i++)dfs(lt[p][i]),sz[p]+=sz[lt[p][i]];
sort(lt[p],lt[p]+cs[p],[](int a,int b){return sz[a]>sz[b];});
}
void cfs(int p,int o){
if(cs[p]==0){
for(int i=1;i<=n-1;i++)dp[o][1][i]=lv[p]*i,dp[o][0][i]=lv[p]*lv[p];
}else{
cfs(lt[p][0],o);
int x=sz[lt[p][0]],y=n-sz[lt[p][0]];
for(int _=1;_<cs[p];_++){
cfs(lt[p][_],o+1);
for(int i=0;i<=x+sz[lt[p][_]];i++)for(int j=1;j<=y-sz[lt[p][_]];j++)f[i][j]=1e18;
for(int i=0;i<=x;i++)for(int j=1;j<=y;j++)for(int k=0;k<=sz[lt[p][_]]&&k<j;k++)
f[i+k][j-k]=min(f[i+k][j-k],dp[o][i][j]+dp[o+1][k][i+j-k]);
x+=sz[lt[p][_]],y-=sz[lt[p][_]];
for(int i=0;i<=x;i++)for(int j=1;j<=y;j++)dp[o][i][j]=f[i][j];
}
if(p==1){
for(int i=0;i<=n-1;i++)res=min(res,dp[o][i][1]);
}else{
for(int i=0;i<=sz[p];i++)for(int j=1;j<=n-sz[p];j++)f[i][j]=1e18;
for(int i=1;i<=sz[p];i++)for(int j=1;j<=n-sz[p];j++)
f[i][j]=min(f[i][j],dp[o][i-1][j+1]+lv[p]*i*j);
for(int i=0;i<=sz[p]-1;i++)f[0][1]=min(f[0][1],dp[o][i][1]+lv[p]*lv[p]);
for(int i=2;i<=n-sz[p];i++)f[0][i]=f[0][1];
for(int i=0;i<=sz[p];i++)for(int j=1;j<=n-sz[p];j++)dp[o][i][j]=f[i][j];
}
}
}
int main(){
cin>>n,res=1e18;
for(int i=2,a;i<=n;i++)cin>>a>>lv[i],lt[a][cs[a]++]=i;
dfs(1),cfs(1,0),cout<<res;
return 0;
}
#include<iostream>//不能过的内存 O(n^3)
#include<cstdio>//用vector节省空间实际也可以过
using namespace std;
#define long long long
int n,lt[510][510],sz[510],cs[510];
long dp[510][510][510],f[2][510][510],lv[510];
void cfs(int p){
sz[p]=1;
for(int i=0;i<cs[p];i++)cfs(lt[p][i]),sz[p]+=sz[lt[p][i]];
if(cs[p]==0){
for(int i=1;i<=n-1;i++)dp[p][1][i]=lv[p]*i,dp[p][0][i]=lv[p]*lv[p];
}else{
int b=1,x=sz[lt[p][0]],y=n-sz[lt[p][0]];
for(int i=0;i<=x;i++)for(int j=1;j<=y;j++)f[b][i][j]=dp[lt[p][0]][i][j];
for(int _=1;_<cs[p];_++){
for(int i=0;i<=x+sz[lt[p][_]];i++)for(int j=1;j<=y-sz[lt[p][_]];j++)f[!b][i][j]=1e18;
for(int i=0;i<=x;i++)for(int j=1;j<=y;j++)for(int k=0;k<=sz[lt[p][_]]&&k<j;k++)
f[!b][i+k][j-k]=min(f[!b][i+k][j-k],f[b][i][j]+dp[lt[p][_]][k][i+j-k]);
x+=sz[lt[p][_]],y-=sz[lt[p][_]],b=!b;
}
for(int i=1;i<=sz[p];i++)for(int j=1;j<=n-sz[p];j++)
dp[p][i][j]=min(dp[p][i][j],f[b][i-1][j+1]+lv[p]*i*j);
for(int i=0;i<=sz[p]-1;i++)dp[p][0][1]=min(dp[p][0][1],f[b][i][1]+lv[p]*lv[p]);
for(int i=2;i<=n-sz[p];i++)dp[p][0][i]=dp[p][0][1];
}
}
int main(){
cin>>n;
for(int i=2,a;i<=n;i++)cin>>a>>lv[i],lt[a][cs[a]++]=i;
for(int i=1;i<=n;i++)for(int j=0;j<=n;j++)for(int k=0;k<=n;k++)dp[i][j][k]=1e18;
cfs(1),cout<<dp[1][0][1];
return 0;
}

浙公网安备 33010602011771号