【BZOJ5290】 [Hnoi2018]道路

BZOJ5290 [Hnoi2018]道路


前言

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

我太菜了.

Solution

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

k0iDpt.md.png

那么就可以树形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
",dp[1][0][0]);
	return 0;
}
原文地址:https://www.cnblogs.com/mleautomaton/p/10368018.html