洛谷 P4438 [HNOI/AHOI2018]道路 解题报告
给你一个二叉树。
每个叶节点的贡献为:ci*(左边标记次数+ai)*(右边标记次数+bi)。
使该贡献最小。
考虑树形dp(记搜)。
IDEA:
- 搜到叶节点时直接计算值返回。
- 搜到已搜过的节点直接返回。
- 否则,考虑两种情况:将左边标记/将右边标记。
(题解区大佬的压空间有点看不太懂。于是蒟蒻就选择了这种方法……代码也很短x.)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> #define ll long long using namespace std; inline void read(ll &a) {a=0;int c=getchar(),b=1; while(c>'9'||c<'0') {if(c=='-')b=-1;c=getchar();} while(c>='0'&&c<='9') a=(a<<3)+(a<<1)+c-48,c=getchar();a*=b; } /**/ const int N=20005; const ll inf=0x3f3f3f3f3f3f3f3fll; ll n,a[N],b[N],c[N],ls[N],rs[N],f[N][45][45]; /**/ ll dfs(ll u,ll x,ll y) { if(f[u][x][y]!=inf) return f[u][x][y]; if(u<0) return c[-u]*(a[-u]+x)*(b[-u]+y); return f[u][x][y]=min(dfs(ls[u],x,y)+dfs(rs[u],x,y+1),dfs(ls[u],x+1,y)+dfs(rs[u],x,y)); } int main() { memset(f,0x3f,sizeof(f)); read(n); for(int i=1;i<n;i++) read(ls[i]),read(rs[i]); for(int i=1;i<=n;i++) read(a[i]),read(b[i]),read(c[i]); cout<<dfs(1,0,0); }