Evanyou Blog 彩带

P4438 [HNOI/AHOI2018]道路

题目描述

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

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

c_i \cdot (a_i + x) \cdot (b_i + y)ci(ai+x)(bi+y)

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

输入输出格式

输入格式:

 

第一行为正整数 nn 。

接下来 n - 1n1 行,每行描述一个城市。其中第 ii 行包含两个数 s_i,t_isi,ti 。 s_isi 表示通向第 ii 座城市 的公路的起点, t_iti表示通向第i座城市的铁路的起点。如果 s_i > 0si>0 ,那么存在一条从第 s_isi 座城 市通往第 ii 座城市的公路,否则存在一条从第 -s_isi 个乡村通往第i座城市的公路; t_iti 类似地,如 果 t_i > 0ti>0 ,那么存在一条从第 t_iti​ 座城市通往第i座城市的铁路,否则存在一条从第 -t_i−ti​ 个乡村通 往第 ii 座城市的铁路。

接下来 nn 行,每行描述一个乡村。其中第i行包含三个数 a_i,b_i,c_iai,bi,ci ,其意义如题面所示。

 

输出格式:

 

输出一行一个整数,表示最优翻修方案的不便利值。

 

输入输出样例

输入样例#1: 
6 
2 3 
4 5 
-1 -2 
-3 -4 
-5 -6 
1 2 3 
1 3 2 
2 1 3 
2 3 1 
3 1 2 
3 2 1
输出样例#1: 
54
输入样例#2: 
9 
2 -2 
3 -3 
4 -4 
5 -5 
6 -6 
7 -7 
8 -8 
-1 -9 
1 60 1 
1 60 1 
1 60 1 
1 60 1 
1 60 1 
1 60 1 
1 60 1 
1 60 1 
1 60 1
输出样例#2: 
548
输入样例#3: 
12 
2 4 
5 3 
-7 10 
11 9 
-1 6 
8 7 
-6 -10 
-9 -4
-12 -5 
-2 -3 
-8 -11 
53 26 491 
24 58 190 
17 37 356 
15 51 997 
30 19 398 
3 45 27 
52 55 838 
16 18 931 
58 24 212 
43 25 198 
54 15 172 
34 5 524
输出样例#3: 
5744902
 

说明

【样例解释 1】

如图所示,我们分别用蓝色、黄色节点表示城市、乡村;用绿色、红色箭头分别表示 公路、铁路;用加粗箭头表示翻修的道路。

一种不便利值等于54的方法是:翻修通往城市2和城市5的铁路,以及通往其他城市的 公路。用→和⇒表示公路和铁路,用∗→和∗⇒表示翻修的公路和铁路,那么:

编号为1的乡村到达首都的路线为:-1 ∗→ 3 ⇒ 1,经过0条未翻修公路和1条未翻修铁 路,代价为3 × (1 + 0) × (2 + 1) = 9;
编号为2的乡村到达首都的路线为:-2 ⇒ 3 ⇒ 1,经过0条未翻修公路和2条未翻修铁 路,代价为2 × (1 + 0) × (3 + 2) = 10;
编号为3的乡村到达首都的路线为:-3 ∗→ 4 → 2 ∗→ 1,经过1条未翻修公路和0条未 翻修铁路,代价为3 × (2 + 1) × (1 + 0) = 9;
编号为4的乡村到达首都的路线为:-4 ⇒ 4 → 2 ∗→ 1,经过1条未翻修公路和1条未翻 修铁路,代价为1 × (2 + 1) × (3 + 1) = 12;
编号为5的乡村到达首都的路线为:-5 → 5 ∗⇒ 2 ∗→ 1,经过1条未翻修公路和0条未 翻修铁路,代价为2 × (3 + 1) × (1 + 0) = 8;
编号为6的乡村到达首都的路线为:-6 ∗⇒ 5 ∗⇒ 2 ∗→ 1,经过0条未翻修公路和0条未翻修铁路,代价为1 × (3 + 0) × (2 + 0) = 6;

总的不便利值为9 + 10 + 9 + 12 + 8 + 6 = 54。可以证明这是本数据的最优解。

【样例解释 2】

在这个样例中,显然应该翻修所有公路。

【数据范围】 一共20组数据,编号为1 ∼ 20。 对于编号 \le 44 的数据, n \le 20n20 ;
对于编号为5 ∼ 8的数据, a_i,b_i,c_i \le 5ai,bi,ci5 , n \le 50n50 ;
对于编号为9 ∼ 12的数据, n \le 2000n2000 ;
对于所有的数据, n \le 20000n20000 , 1 \le a_i,b_i \le 601ai,bi60 , 1 \le c_i \le 10^91ci109 , s_i,t_isi,ti 是 [-n,-1] \cup (i,n - 1][−n,−1]∪(i,n−1] 内的整数,任意乡村可以通过不超过40条道路到达首都。

 

 

Solution:  

  本题是今年湖南省选的魔性Day2的T3。  今天集训又考了本题的改编,记得当时考场上本题原题就没想出来,于是今天补个坑。  题目中有个特别重要的信息:一定是一棵以$1$为根的二叉树,且每个节点到两个子儿子的边中必须且只能删去其中一条。  于是,对于每个节点,要么删与其相连的公路,要么删与其相连的铁路。
  所以定义状态$f[i][j][k]$表示从下往上删到$i$节点时,有$j$条公路和$k$条铁路没被删的最少代价。
  那么目标状态就是$f[1][0][0]$,初始化直接为$0$,对于所有的叶子节点$p$都有$f[p][i][j]=c[p]\times (a[p]+i)\times (b[p]+j)$。
  不难得到状态转移方程:$f[u][i][j]=min(f[s[u]][i][j]+f[t[u]][i][j+1],f[s[u]][i+1][j]+f[t[u]][i][j+1])$(其中$s[u]$表示到$u$的公路所连节点,$t[u]$则表示铁路所连节点)
  ps:本题巨佬们的$DP$写法都十分的诡异,我的是卡着空间和时间过的,可以学习一下巨佬们的$DP$思路。
代码:

 

#include<bits/stdc++.h>
#define il inline
#define ll long long
#define For(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
#define Bor(i,a,b) for(int (i)=(b);(i)>=(a);(i)--)
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)>(b)?(b):(a))
using namespace std;
const int N=40001;
int s[N],t[N],a[N],b[N],c[N],d[N];
ll n,f[N][41][41];


il int gi(){
    int a=0;char x=getchar();bool f=0;
    while((x<'0'||x>'9')&&x!='-')x=getchar();
    if(x=='-')x=getchar(),f=1;
    while(x>='0'&&x<='9')a=(a<<3)+(a<<1)+x-48,x=getchar();
    return f?-a:a;
}

il void dfs(int u){
    if(u>=n){For(i,0,d[u]) For(j,0,d[u]) f[u][i][j]=1ll*c[u]*(a[u]+i)*(b[u]+j);return;}
    d[s[u]]=d[u]+1,d[t[u]]=d[u]+1;
    dfs(s[u]),dfs(t[u]);
    For(i,0,d[u]) For(j,0,d[u]) f[u][i][j]=min(f[s[u]][i+1][j]+f[t[u]][i][j],f[s[u]][i][j]+f[t[u]][i][j+1]);
}

int main(){
    n=gi();
    For(i,1,n-1) {
        s[i]=gi();t[i]=gi();
        s[i]=s[i]>0?s[i]:n-s[i]-1;
        t[i]=t[i]>0?t[i]:n-t[i]-1;
    }
    For(i,n,(n<<1)-1) a[i]=gi(),b[i]=gi(),c[i]=gi();
    dfs(1);
    printf("%lld\n",f[1][0][0]);
    return 0;
}

 

posted @ 2018-06-23 21:00  five20  阅读(380)  评论(0编辑  收藏  举报
Live2D