题目描述
W 国的交通呈一棵树的形状。W 国一共有 n−1 n − 1 个城市和 n n 个乡村,其中城市从 到 n−1 n − 1 编号,乡村从 1 1 到 编号,且 1 1 号城市是首都。道路都是单向的,本题中我们只考虑从乡村通往首都的道路网络。对于每一个城市,恰有一条公路和一条铁路通向这座城市。对于城市i, 通向该城市的道路(公路或铁路)的起点,要么是一个乡村,要么是一个编号比 大的城市。 没有道路通向任何乡村。除了首都以外,从任何城市或乡村出发只有一条道路;首都没有往 外的道路。从任何乡村出发,沿着唯一往外的道路走,总可以到达首都。
W 国的国王小 W 获得了一笔资金,他决定用这笔资金来改善交通。由于资金有限,小 W 只能翻修 n−1 n − 1 条道路。小 W 决定对每个城市翻修恰好一条通向它的道路,即从公路和铁 路中选择一条并进行翻修。小 W 希望从乡村通向城市可以尽可能地便利,于是根据人口调 查的数据,小 W 对每个乡村制定了三个参数,编号为 i i 的乡村的三个参数是 , bi b i 和 ci c i 。假设 从编号为 i i 的乡村走到首都一共需要经过 条未翻修的公路与 y y 条未翻修的铁路,那么该乡村 的不便利值为
在给定的翻修方案下,每个乡村的不便利值相加的和为该翻修方案的不便利值。 翻修 n−1 n − 1 条道路有很多方案,其中不便利值最小的方案称为最优翻修方案,小 W 自然 希望找到最优翻修方案,请你帮助他求出这个最优翻修方案的不便利值。
输入输出格式
输入格式:第一行为正整数 n n 。
接下来 行,每行描述一个城市。其中第 i i 行包含两个数 。 si s i 表示通向第 i i 座城市 的公路的起点, 表示通向第i座城市的铁路的起点。如果 si>0 s i > 0 ,那么存在一条从第 si s i 座城 市通往第 i i 座城市的公路,否则存在一条从第 个乡村通往第i座城市的公路; 类似地,如 果 ti<0 t i < 0 ,那么存在一条从第 ti t i 座城市通往第i座城市的铁路,否则存在一条从第 −ti − t i 个乡村通 往第 i i 座城市的铁路。
接下来 行,每行描述一个乡村。其中第i行包含三个数 ai,bi,ci a i , b i , c i ,其意义如题面所示。
输出格式:输出一行一个整数,表示最优翻修方案的不便利值。
输入输出样例
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
548
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
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。 对于编号
≤4
≤
4
的数据,
n≤20
n
≤
20
;
对于编号为5 ∼ 8的数据,
ai,bi,ci≤5
a
i
,
b
i
,
c
i
≤
5
,
n≤50
n
≤
50
;
对于编号为9 ∼ 12的数据,
n≤2000
n
≤
2000
;
对于所有的数据,
n≤20000
n
≤
20000
,
1≤ai,bi≤60
1
≤
a
i
,
b
i
≤
60
,
1≤ci≤109
1
≤
c
i
≤
10
9
,
si,ti
s
i
,
t
i
是
[−n,−1]∪(i,n−1]
[
−
n
,
−
1
]
∪
(
i
,
n
−
1
]
内的整数,任意乡村可以通过不超过40条道路到达首都。
看到公式感觉很不可做啊…然后发现保证树的深度不超过40层, 那么直接暴力DP即可。
对于每个乡村, 我们计算 dp[i][j][k] d p [ i ] [ j ] [ k ] 为第 i i 个乡村经过条未翻修的铁路和 k k 条未翻修的公路的不便利值。
对于每个城市, 我们有如下公式:
即修铁路和修公路的最优值。 最后答案即为dp[1][0][0]。
不过因为深度不超过40, 我们可以滚动第一维, 卡进三级缓存, 实测比不滚动快5倍左右。
代码如下:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cctype>
#include <algorithm>
#include <cstdlib>
using namespace std;
#define R register
#define gc getchar()
#define IN inline
#define W while
#define MX 50005
#define bound 20005
#define ll long long
#define ls tree[now].son[0]
#define rs tree[now].son[1]
static bool neg; static char c;
template <class T>
IN void in (T &x)
{
x = 0; c = gc;
W (!isdigit(c))
{if(c == '-') neg = true; c = gc;}
W (isdigit(c))
{x = (x << 1) + (x << 3) + c - 48, c = gc;}
if(neg) x = -x, neg = false;
}
template <class T>
IN T minn (const T &x, const T &y)
{return x < y ? x : y;}
struct Node
{
int son[2];
ll a, b, c;
}tree[MX];
ll dp[85][45][45];
int dot, st[200], tot, id[MX], top;
void DFS(int now, int x, int y)
{
id[now] = top ? st[top--] : ++tot;//分配滚动数组下表, 若有以前用过的则复用
if(!ls)//对于叶节点
{
for (R int i = 0; i <= x; ++i)
for (R int j = 0; j <= y; ++j)
dp[id[now]][i][j] = tree[now].a * (tree[now].b + i) * (tree[now].c + j);
return;
}
DFS(ls, x + 1, y);
DFS(rs, x, y + 1);
for (R int i = 0; i <= x; ++i)
for (R int j = 0; j <= y; ++j)//暴力转移
dp[id[now]][i][j] = minn(dp[id[ls]][i + 1][j] + dp[id[rs]][i][j], dp[id[ls]][i][j] + dp[id[rs]][i][j + 1]);
st[++top] = id[ls], st[++top] = id[rs];//将下标回收
}
int main(void)
{
int a, b;
in(dot);
for (R int i = 1; i < dot; ++i)
{
in(a), in(b);
tree[i].son[0] = a > 0 ? a : -a + bound;
tree[i].son[1] = b > 0 ? b : -b + bound;
}
for (R int i = 1; i <= dot; ++i)
in(tree[i + bound].b), in(tree[i + bound].c), in(tree[i + bound].a);
DFS(1, 0, 0);
printf("%lld", dp[id[1]][0][0]);
}
这样的题作为省选题也太毒(shui)了吧…