【BZOJ5290】 [Hnoi2018]道路

时间:2023-03-08 22:04:37

BZOJ5290 [Hnoi2018]道路


前言

这道题目我竟然没有在去年省选切?

我太菜了.

Solution

对题面进行一个语文透彻解析,发现这是一个二叉树,乡村都是叶子节点,城市都有两个儿子.(依据在下)

【BZOJ5290】 [Hnoi2018]道路

那么就可以树形dp了.我们假设公路是左儿子,铁路是右儿子.

\(dp_{i,j,k}\)表示到了\(i\)节点,经过了\(j\)条未翻修的公路,经过了\(k\)条未翻修的铁路.

考虑对于不同的\(i\)怎么计算.

  1. 叶子节点,直接计算贡献即可.

  2. 非叶子节点,考虑要么不翻修公路,要么不翻修铁路,直接从左右儿子的两种情况转移就好了.

具体的转移方程参考代码.

代码实现

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<iostream>
using namespace std;
#define ll long long
#define re register
#define file(a) freopen(a".in","r",stdin);freopen(a".out","w",stdout)
inline int gi()
{
    int f=1,sum=0;char ch=getchar();
    while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0' && ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
    return f*sum;
}
const int N=40010;
ll dp[510][41][41];
int son[N][2],c[N<<1],b[N<<1],a[N<<1],n,id[N<<1],sta[N<<1],tot,top;
void dfs(int u,int x,int y)
{
    int p=id[u]=top?sta[top--]:++tot;
    if(!son[u][0])
    {
        for(int i=0;i<=x;i++)
            for(int j=0;j<=y;j++)
                dp[p][i][j]=1ll*c[u]*(a[u]+i)*(b[u]+j);
        return;
    }
    dfs(son[u][0],x+1,y);dfs(son[u][1],x,y+1);
    int ls=id[son[u][0]],rs=id[son[u][1]];
    for(int i=0;i<=x;i++)
        for(int j=0;j<=y;j++)
            dp[p][i][j]=min(dp[ls][i][j]+dp[rs][i][j+1],dp[ls][i+1][j]+dp[rs][i][j]);
    sta[++top]=ls;sta[++top]=rs;//重复利用了.
}
int main()
{
    n=gi();
    for(int i=1;i<n;i++)
    {
        int s=gi(),t=gi();
        if(s<0)s=-s+n-1;
        if(t<0)t=-t+n-1;
        son[i][0]=s;son[i][1]=t;
    }
    for(int i=n;i<=n+n-1;i++)
        a[i]=gi(),b[i]=gi(),c[i]=gi();
    dfs(1,0,0);
    printf("%lld\n",dp[1][0][0]);
    return 0;
}