[HNOI/AHOI2018]道路
P4438 [HNOI/AHOI2018]道路
题意:
从叶子节点到根节点,会经过 \(i\) 条烂公路 \(j\) 条烂铁路。
每个叶子节点的贡献为 \(c[i]*(a[i]+i) * (b[i]+j)\)
我们可以翻修 \(n-1\) 条路,求 \(\sum_{叶子节点 i} c[i]*(a[i]+i) * (b[i]+j)\) 最小。
解决:
设 \(f[x][i][j]\) 为从根节点到节点 \(x\) ,经过 \(i\) 公路, \(j\) 铁路
因为有左儿子,右儿子的存在,所以我们通过 \(dfs\) 序 \(dfn\) 进行标号,从而使 \(dp\) 更方便。
对 叶子节点 进行预处理:
\[dp[dfn[x]][i][j]=c[x]*(a[x]+i)*(b[x]+j);
\]
如果选择修公路,右儿子经过一条未修的铁路:
\[dp[dfn[x]][i][j]=dp[dfn[son[x][0]]][i][j]+dp[dfn[son[x][1]]][i][j+1]
\]
如果选择修铁路,左儿子经过一条未修的公路:
\[dp[dfn[x]][i][j]=dp[dfn[son[x][0]]][i+1][j]+dp[dfn[son[x][1]]][i][j]
\]
一起取 \(min\) ,顺序进行枚举即可算出答案。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+5,M=2e4+5;
int n,m;
int nxt[N],ver[N],tot,head[N];
int a[N],b[N],c[N];
int dp[105][45][45];
int dfn[N],sizes[N],dep[N];
vector<int> son[M];
void add(int x,int y){
ver[++tot]=y;nxt[tot]=head[x];head[x]=tot;
}
void dfs(int x,int k){
dfn[x]=k; //通过dfs序排节点
sizes[x]=1;
for(int i=head[x];i;i=nxt[i]){
int y=ver[i];
dep[y]=dep[x]+1;
son[x].push_back(y);
if(son[x][1]) dfs(y,k+2);
else dfs(y,k+1);
sizes[x]+=sizes[y];
}
if(sizes[x]==1) //叶子节点,乡村预处理
for(int i=0;i<=dep[x];i++) for(int j=0;j<=dep[x];j++)
dp[dfn[x]][i][j]=c[x]*(a[x]+i)*(b[x]+j);
else
for(int i=0;i<=dep[x];i++) for(int j=0;j<=dep[x];j++) //选择修公路,右儿子多经过一条未修的道路,修铁路,左儿子经过一条未修的公路,取最小值即可。
dp[dfn[x]][i][j]=min(dp[dfn[son[x][0]]][i][j]+dp[dfn[son[x][1]]][i][j+1],dp[dfn[son[x][0]]][i+1][j]+dp[dfn[son[x][1]]][i][j]);
}
signed main(){
cin>>n; m=2*n-1; memset(dp,0x3f3f3f3f,sizeof(dp));
for(int i=1,x,y;i<n;i++){
scanf("%lld%lld",&x,&y);
if(y>=0) add(i,y); else add(i,-y+n-1);
if(x>=0) add(i,x); else add(i,-x+n-1);
}
for(int i=n;i<=m;i++) scanf("%lld%lld%lld",&a[i],&b[i],&c[i]);
dep[1]=0;
dfs(1,0);
printf("%lld\n",dp[dfn[1]][0][0]);
system("pause");
return 0;
}
不关注的有难了😠😠😠https://b23.tv/hoXKV9