[Hnoi2018]道路——树形DP

Description:

W 国的交通呈一棵树的形状。W 国一共有 n1 n − 1 个城市和 n n 个乡村,其中城市从 1 n1 n − 1 编号,乡村从 1 1 n 编号,且 1 1 号城市是首都。道路都是单向的,本题中我们只考虑从乡村通往首都的道路网络。对于每一个城市,恰有一条公路和一条铁路通向这座城市。对于城市i, 通向该城市的道路(公路或铁路)的起点,要么是一个乡村,要么是一个编号比 i 大的城市。 没有道路通向任何乡村。除了首都以外,从任何城市或乡村出发只有一条道路;首都没有往 外的道路。从任何乡村出发,沿着唯一往外的道路走,总可以到达首都。

W 国的国王小 W 获得了一笔资金,他决定用这笔资金来改善交通。由于资金有限,小 W 只能翻修 n1 n − 1 条道路。小 W 决定对每个城市翻修恰好一条通向它的道路,即从公路和铁 路中选择一条并进行翻修。小 W 希望从乡村通向城市可以尽可能地便利,于是根据人口调 查的数据,小 W 对每个乡村制定了三个参数,编号为 i i 的乡村的三个参数是 ai​, bi b i ​ 和 ci c i 。假设 从编号为 i i 的乡村走到首都一共需要经过 x 条未翻修的公路与 y y 条未翻修的铁路,那么该乡村 的不便利值为

ci(ai+x)(bi+y)

在给定的翻修方案下,每个乡村的不便利值相加的和为该翻修方案的不便利值。 翻修 n1 n − 1 条道路有很多方案,其中不便利值最小的方案称为最优翻修方案,小 W 自然 希望找到最优翻修方案,请你帮助他求出这个最优翻修方案的不便利值。

思路:

大家都说这题是一道普及DP,但也没有看到有很多人在考场A啊。所以这种题还是要引起我们的注意。
考虑树形DP,考虑对与每一颗子树,我们求它的所有的叶子节点的最小权值。由于这是一颗二叉树,所以我们可以从两个子节点转移到父节点,即对于每一个父节点枚举修建哪一条边。但是还要使得我们的选择是合法的,即对于左儿子和右儿子的最小的权值和的情况满足从他们到根节点的情况一致。所以我们可以设置DP的状态了,即 dpi,j,k d p i , j , k 表示第 i i 个节点以上的路径里有i未修建的公路和 j j 条未修建的铁路。转移方程就很好地出来了。

dpu,j,k=min(dplc,j,k+dprc,j,k+1,dplc,j+1,k+dprc,j,k)

另外,好像要用滚动数组卡一下空间。。。

/*========================
 * Auhtor : ylsoi
 * Problem : road
 * Algorithm : DP
 * Time : 2018.4.24
 * =====================*/
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
void File(){
    freopen("road.in","r",stdin);
    freopen("road.out","w",stdout);
}
#define REP(i,a,b) for(register int i=a;i<=b;++i)
#define DREP(i,a,b) for(register int i=a;i>=b;--i)
#define MREP(i,x) for(register int i=beg[x];i;i=E[i].last)
#define ll long long
const int maxn=4e4+1;
const int maxd=40+1;
int n,ch[maxn][2],a[maxn],b[maxn],c[maxn],cnt,be[maxn];
ll dp[maxn/2+1][maxd][maxd];
#define lc ch[u][0]
#define rc ch[u][1]
ll _min(ll x,ll y){return x<y ? x : y;}
void dfs(int u,int cnt0,int cnt1){
    if(!lc && !rc){
        REP(i,0,cnt0)REP(j,0,cnt1)
            dp[be[u]][i][j]=(ll)c[u]*(a[u]+i)*(b[u]+j);
        return;
    }
    be[lc]=++cnt;be[rc]=++cnt;
    dfs(lc,cnt0+1,cnt1);
    dfs(rc,cnt0,cnt1+1);
    REP(j,0,cnt0)REP(k,0,cnt1)
        dp[be[u]][j][k]=_min(dp[be[lc]][j][k]+dp[be[rc]][j][k+1],dp[be[lc]][j+1][k]+dp[be[rc]][j][k]);
    cnt-=2;
}
int main(){
    File();
    scanf("%d",&n);
    REP(i,1,n-1){
        int s,t;
        scanf("%d%d",&s,&t);
        if(s>0)ch[i][0]=s;
        else ch[i][0]=-s+n-1;
        if(t>0)ch[i][1]=t;
        else ch[i][1]=-t+n-1;
    }
    REP(i,1,n)scanf("%d%d%d",&a[i+n-1],&b[i+n-1],&c[i+n-1]);
    be[1]=++cnt;
    dfs(1,0,0);
    printf("%lld\n",dp[1][0][0]);
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据引用\[1\]和引用\[2\]的描述,题目中的影魔拥有n个灵魂,每个灵魂有一个战斗力ki。对于任意一对灵魂对i,j (i<j),如果不存在ks (i<s<j)大于ki或者kj,则会为影魔提供p1的攻击力。另一种情况是,如果存在一个位置k,满足ki<c<kj或者kj<c<ki,则会为影魔提供p2的攻击力。其他情况下的灵魂对不会为影魔提供攻击力。 根据引用\[3\]的描述,我们可以从左到右进行枚举。对于情况1,当扫到r\[i\]时,更新l\[i\]的贡献。对于情况2.1,当扫到l\[i\]时,更新区间\[i+1,r\[i\]-1\]的贡献。对于情况2.2,当扫到r\[i\]时,更新区间\[l\[i\]+1,i-1\]的贡献。 因此,对于给定的区间\[l,r\],我们可以根据上述方法计算出区间内所有下标二元组i,j (l<=i<j<=r)的贡献之和。 #### 引用[.reference_title] - *1* *3* [P3722 [AH2017/HNOI2017]影魔(树状数组)](https://blog.csdn.net/li_wen_zhuo/article/details/115446022)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [洛谷3722 AH2017/HNOI2017 影魔 线段树 单调栈](https://blog.csdn.net/forever_shi/article/details/119649910)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值