Problem:
G: トレジャーハンター (Treasure Hunter)

問題

財宝の山が N 個あり、それぞれの山は 1 から N までで番号づけられている。i 番目の山には価値 p_i のお宝が眠っており、このお宝はその山に訪れた時点で得ることができる。一度お宝を得るとその山からはお宝がなくなってしまうので、お宝はそれぞれ一度しか得ることはできない。

異なる山に移動するには必ず道を利用しなければならない。山と山の間には合計 N-1 本の道があり、i 番目の道は山 u_i と v_i を双方向に結んでいる。もしもどの道も問題なく通行可能であると仮定すると、任意の 2 つの山について相互に行き来可能であることが分かっている。

どの道も長らく誰も通行しておらず修復しないと渡れないため、i 番目の道について最初に渡る際には c_i 円を支払って工事し、通行可能にする必要がある。一度工事した道であればそれ以上お金を支払うことなく通行が可能である。

トレジャーハンターであるあなたは、道の工事費用の予算を W 円持っている。あなたの目的は、はじめに降り立つ山を任意に 1 つ決め、合計 W 円以下で道を工事して異なる山に移動することで、得られるお宝の価値の合計を最大化することである。あなたは最大でどれだけの価値を得られるだろうか？

なお、お宝を途中で換金することはできないため、お宝を工事費用として使用することはできないことに注意せよ。

入力形式

入力は以下の形式で与えられる。

N W
p_1 p_2 ... p_N
u_1 v_1 c_1
:
u_{N-1} v_{N-1} c_{N-1}

1 行目では、山の数 N と工事費用の予算 W が与えられる。

2 行目では、i 番目の山に眠っているお宝の価値 p_i が与えられる。

3 行目から N+1 行目では、道の情報が与えられる。2+i 行目は i 番目の道の情報を表しており、山 u_i と v_i の間に工事費用 c_i の道があることを表す。

制約

1 \leq N \leq 10^2

1 \leq W \leq 10^5

1 \leq p_i \leq 10^9

1 \leq u_i \lt v_i \leq N

i \neq j ならば u_i \neq u_j または v_i \neq v_j

1 \leq c_i \leq 10^5

与えられる入力は全て整数である

出力形式

得られるお宝の価値の合計の最大値を 1 行に出力せよ。末尾の改行を忘れないこと。

入力例 1

3 10
6 8 2
1 2 3
2 3 8

出力例 1

14

山 1 または山 2 に降り立ち、山 1 と山 2 を結ぶ道を工事し、山 1 と山 2 にあるお宝を得るのが最適である。工事費用の予算は 10 であるので、ふたつの道を両方とも工事することはできない。

入力例 2

3 15
10 10 12
1 2 6
1 3 4

出力例 2

32

お宝を全て得ることができる。

入力例 3

5 1
4 8 8 2 10
1 2 3
2 4 5
2 5 2
1 3 7

出力例 3

10

予算が不足しており、どの道も工事できない場合もある。

Solution in Rust (judge verdict: Accepted)

use std::cmp;

const INF: i64 = std::i64::MAX;

fn main() {
    let s = std::io::stdin();
    let mut sc = Scanner { stdin: s.lock() };
    let n: usize = sc.read();
    let w: usize = sc.read();
    let p: Vec<i64> = sc.vec(n);
    let mut graph = vec![vec![]; n];
    for _ in 1..n {
        let u = sc.read::<usize>() - 1;
        let v = sc.read::<usize>() - 1;
        let cost = sc.read::<usize>();
        graph[u].push((v, cost));
        graph[v].push((u, cost));
    }

    let mut tree = vec![vec![]; n];
    tree_dfs(0, 0, &graph, &mut tree);

    let mut ans = -INF;
    for i in 0..n {
        let mut state = vec![-INF; w + 1];
        state[0] = 0;
        let state = rec_dp(i, &state, &tree, w, &p);
        ans = cmp::max(ans, state.into_iter().max().unwrap());
    }
    println!("{}", ans);
}

fn tree_dfs(
    v: usize,
    p: usize,
    graph: &Vec<Vec<(usize, usize)>>,
    tree: &mut Vec<Vec<(usize, usize)>>,
) {
    for &(next, cost) in graph[v].iter() {
        if next == p {
            continue;
        }
        tree_dfs(next, v, graph, tree);
        tree[v].push((next, cost));
    }
}

fn rec_dp(
    v: usize,
    state: &Vec<i64>,
    tree: &Vec<Vec<(usize, usize)>>,
    w: usize,
    values: &Vec<i64>,
) -> Vec<i64> {
    let mut state = state.clone();
    for &(next, cost) in tree[v].iter() {
        let next_state = rec_dp(next, &state, tree, w, values);
        for i in 0..(w + 1) {
            if i + cost > w {
                break;
            }
            state[i + cost] = cmp::max(next_state[i], state[i + cost]);
        }
    }

    for i in 0..(w + 1) {
        state[i] += values[v];
    }
    state
}

pub struct Scanner<R> {
    stdin: R,
}

impl<R: std::io::Read> Scanner<R> {
    pub fn read<T: std::str::FromStr>(&mut self) -> T {
        use std::io::Read;
        let buf = self
            .stdin
            .by_ref()
            .bytes()
            .map(|b| b.unwrap())
            .skip_while(|&b| b == b' ' || b == b'\n')
            .take_while(|&b| b != b' ' && b != b'\n')
            .collect::<Vec<_>>();
        unsafe { std::str::from_utf8_unchecked(&buf) }
            .parse()
            .ok()
            .expect("Parse error.")
    }
    pub fn vec<T: std::str::FromStr>(&mut self, n: usize) -> Vec<T> {
        (0..n).map(|_| self.read()).collect()
    }
    pub fn chars(&mut self) -> Vec<char> {
        self.read::<String>().chars().collect()
    }
}