题意:
分析:
这个题意好阴间,转化一下大概就是,给定一颗二叉树,保证有 (n) 个叶子结点,(n-1) 个非叶子结点,每个点有两种边,求给定式子的最小值
- 假做法
我原本以为这个式子拆开之后可以分别计算贡献
(c_u(a_u+x)(b_u+y)=a_ub_uc_u+c_ua_uy+a_ub_ux+c_uxy)
开一个三元组分别统计 (x,y) 状态下的三个参数,但是这个贡献没有办法判断最优所以没法转移
- 正解:
我们发现刚才的式子没法转移是因为我们没有办法考虑加 一条公路/铁路带来的影响 ,那么我们的 DP 就需要避免这种情况,直接把一个点的所有贡献统计完,这也就要求我们必须知道它到根节点的路径上两种边的数目,而且转移还必须从下向上,因此我们得出另一种DP的方式 , (f[u][i][j]) 表示 (u) 到根节点的路径上有 (i) 条公路 (j) 条铁路,易得转移式如下:
(f[u][i][j]=min(f[lc[u]][i][j]+f[rc[u][i]][j+1],f[lc[u]][i+1][j]+f[rc[u]][i][j]))
最后的答案就是 (f[1][0][0])
但是做到这里还没有完,如果完全按照这种方式 DP 的话空间复杂度是 (2n*40^2) 大约在 (3e7) 级别的 (long long) 会炸空间,所以我们要优化 DP ,,我们发现 DP 的时候只与 一条链有关,所以我们给链上的每一个点重标号,这样 DP 数组的第一维只要开到树上最长链的长度 (2*40) 就够了
代码:
#include<bits/stdc++.h>
#define inl inline
#define reg register
using namespace std;
namespace zzc
{
typedef long long ll;
inl ll read()
{
ll x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-48;ch=getchar();}
return x*f;
}
const ll maxn = 1e5+5;
ll n;
ll lc[maxn],rc[maxn],a[maxn],b[maxn],c[maxn],dfn[maxn],dep[maxn];
long long f[105][50][50];
void dfs(ll u,ll k)
{
dfn[u]=k;
if(lc[u])
{
dep[lc[u]]=dep[rc[u]]=dep[u]+1;
dfs(lc[u],k+1);dfs(rc[u],k+2);
for(ll i=0;i<=dep[u];i++)
{
for(ll j=0;j<=dep[u];j++)
{
f[dfn[u]][i][j]=min(f[dfn[lc[u]]][i][j]+f[dfn[rc[u]]][i][j+1],f[dfn[lc[u]]][i+1][j]+f[dfn[rc[u]]][i][j]);
}
}
}
else
{
for(ll i=0;i<=dep[u];i++)
{
for(ll j=0;j<=dep[u];j++)
{
f[dfn[u]][i][j]=1ll*c[u]*(a[u]+i)*(b[u]+j);
}
}
}
}
void work()
{
memset(f,0x3f,sizeof(f));
n=read();
for(ll i=1;i<n;i++)
{
lc[i]=read();rc[i]=read();
if(lc[i]<0) lc[i]=-lc[i]+n-1;
if(rc[i]<0) rc[i]=-rc[i]+n-1;
}
for(ll i=n;i<=2*n-1;i++) a[i]=read(),b[i]=read(),c[i]=read();
dfs(1,0);
printf("%lld
",f[dfn[1]][0][0]);
}
}
int main()
{
zzc::work();
return 0;
}