extern crate core;

use std::fmt;
use std::cmp::{max, min, Ordering};
use std::collections::{BinaryHeap, BTreeMap};
macro_rules! read_line{
    () => {{
        let mut line = String::new();
        std::io::stdin().read_line(&mut line).ok();
        line
    }};
    (delimiter: ' ') => {
        read_line!().split_whitespace().map(|x|x.to_string()).collect::<Vec<_>>()
    };
    (delimiter: $p:expr) => {
        read_line!().split($p).map(|x|x.to_string()).collect::<Vec<_>>()
    };
    (' ') => {
        read_line!(delimiter: ' ')
    };
    ($delimiter:expr) => {
        read_line!(delimiter: $delimiter)
    };
    (' '; $ty:ty) => {
        read_line!().split_whitespace().map(|x|x.parse::<$ty>().ok().unwrap()).collect::<Vec<$ty>>()
    };
    ($delimiter:expr; $ty:ty) => {
        read_line!($delimiter).into_iter().map(|x|x.parse::<$ty>().ok().unwrap()).collect::<Vec<$ty>>()
    };
}
macro_rules! let_all {
    ($($n:ident:$t:ty),*) => {
        let line = read_line!(delimiter: ' ');
        let mut iter = line.iter();
        $(let $n:$t = iter.next().unwrap().parse().ok().unwrap();)*
    };
}
#[derive(Copy, Clone, Eq, PartialEq, Default)]
struct Searched {
    serial: usize
}
impl Searched {
    fn new() -> Searched {
        Searched{serial: 0}
    }
    fn added(&self, target: usize) -> Searched {
        Searched{serial: self.serial | (1 << target)}
    }
    fn is_searched(&self, target: usize) -> bool {
        self.serial & (1 << target) != 0
    }
}
#[derive(Copy, Clone, Eq, PartialEq, Default)]
struct Coordinate {
    x: i32, y: i32
}
impl Coordinate {
    fn new(x: i32, y: i32) -> Coordinate {
        Coordinate{x: x, y: y}
    }
    fn manhattan_distance(&self, &other: &Self) -> i32 {
        (self.x - other.x).abs() + (self.y - other.y).abs()
    }
}
trait AbsoluteValue {
    fn abs(&self) -> Self;
}
impl AbsoluteValue for i32 {
    fn abs(&self) -> Self {
        if self < &0 {
            -*self
        }else {
            *self
        }
    }
}
struct ValueWithKey<K, V> {
    key: K, value: V,
}
impl <K, V> Ord for ValueWithKey<K, V> where K: Ord {
    fn cmp(&self, other: &Self) -> Ordering {
        self.key.cmp(&other.key)
    }
}
impl <K, V> PartialOrd for ValueWithKey<K, V> where K: PartialOrd {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        self.key.partial_cmp(&other.key)
    }
}
impl <K, V> PartialEq for ValueWithKey<K, V> where K: PartialEq {
    fn eq(&self, other: &Self) -> bool {
        self.key == other.key
    }
}
impl <K, V> Eq for ValueWithKey<K, V> where K: Eq {}
fn min_time_for_visit(ref town: &Vec<Coordinate>) -> Vec<i32> {
    let mut min_cost: Vec<Vec<i32>> = vec![vec![std::i32::MAX; town.len()]; 1 << town.len()];
    let start = Coordinate::new(0, 0);
    let mut queue: BinaryHeap<ValueWithKey<i32, (usize, Searched)>> = BinaryHeap::new();
    for i in 0 .. town.len() {
        min_cost[Searched::new().added(i).serial][i] = start.manhattan_distance(&town[i]);
        queue.push(ValueWithKey{key: start.manhattan_distance(&town[i]), value: (i, Searched::new().added(i))});
    }
    while let Some(ValueWithKey{key: cost, value:(from, searched)}) = queue.pop() {
        if min_cost[searched.serial][from] == cost {
            for to in 0 .. town.len() {
                if !searched.is_searched(to) && min_cost[searched.added(to).serial][to] > cost + town[from].manhattan_distance(&town[to]) {
                    min_cost[searched.added(to).serial][to] = cost + town[from].manhattan_distance(&town[to]);
                    queue.push(ValueWithKey{key: min_cost[searched.added(to).serial][to], value: (to, searched.added(to))});
                }
            }
        }
    }
    let mut ret = Vec::<i32>::with_capacity(1 << town.len());
    for &ref vec in &min_cost {
        let mut min:i32 = std::i32::MAX;
        for i in 0 .. town.len() {
            if vec[i] < min - town[i].manhattan_distance(&start){
                min = vec[i] + town[i].manhattan_distance(&start);
            }
        }
        ret.push(min);
    }
    ret
}

struct GoodsInfo {
    weight: usize, value: usize
}
#[derive(Copy, Clone)]
struct TradeInfo {
    goods_id: usize, benefit: usize
}
fn main() {
    let_all!(n: usize, m: usize, w: usize, t: usize);
    let mut goods_to_usize = BTreeMap::<String, usize>::new();
    let mut goods = Vec::with_capacity(m);
    for i in 0 .. m {
        let_all!(name: String, weight: usize, value: usize);
        goods_to_usize.insert(name, i);
        goods.push(GoodsInfo{weight: weight, value: value});
    }
    let goods_to_usize = goods_to_usize;
    let goods = goods;
    let mut town_coordinate = Vec::with_capacity(n);
    let mut town_info = vec![Vec::new(); n];
    for i in 0 .. n {
        let_all!(l: usize, x: i32, y: i32);
        town_coordinate.push(Coordinate{x: x, y: y});
        for _ in 0 .. l {
            let_all!(r: String, q: usize);
            if let Some(&g) = goods_to_usize.get(&r) {
                if goods[g].value > q {
                    town_info[i].push(TradeInfo { goods_id: g, benefit: goods[g].value - q});
                }
            }else {
                unreachable!()
            }
        }
    }
    let town_coordinate = town_coordinate;
    let town_info = town_info;
    let visit_time = min_time_for_visit(&town_coordinate);
    let mut visit_benefit = vec![0; 1 << n];
    for state in 0 .. visit_time.len() {
        let mut max_earn: Vec<i32> = vec![0; m];
        for i in 0 .. n {
            if state & (1 << i) != 0 {
                for &g in &town_info[i] {
                    if max_earn[g.goods_id] < g.benefit as i32 {
                        max_earn[g.goods_id] = g.benefit as i32;
                    }
                }
            }
        }
        let mut max_earn_by_weight = vec![-1 as i64; w + 1];
        max_earn_by_weight[0] = 0;
        let mut max = 0;
        for i in 0 .. w + 1 {
            if max_earn_by_weight[i] != -1 {
                if max < max_earn_by_weight[i] {
                    max = max_earn_by_weight[i];
                }
                for g in 0 .. m {
                    if max_earn[g] > 0 && goods[g].weight + i <= w && max_earn_by_weight[i + goods[g].weight] < max_earn[g] as i64 + max_earn_by_weight[i] {
                        max_earn_by_weight[i + goods[g].weight] = max_earn[g] as i64 + max_earn_by_weight[i];
                    }
                }
            }
        }
        visit_benefit[state] = max;
    }
    let visit_benefit = visit_benefit;
    let mut max_benefit_by_time = vec![-1; t + 1];
    max_benefit_by_time[0] = 0;
    let mut max = 0;
    for i in 0 .. t + 1 {
        if max_benefit_by_time[i] != -1 {
            if max < max_benefit_by_time[i] {
                max = max_benefit_by_time[i];
            }
            for state in 0 .. visit_time.len() {
                if visit_time[state] < max_benefit_by_time.len() as i32 && i as i32 + visit_time[state] < max_benefit_by_time.len() as i32 && max_benefit_by_time[i + visit_time[state] as usize] < max_benefit_by_time[i] + visit_benefit[state] {
                    max_benefit_by_time[i + visit_time[state] as usize] = max_benefit_by_time[i] + visit_benefit[state];
                }
            }
        }
    }
    println!("{}", max);
}


Measured memory_bytes: 5068