#![allow(unused_imports)] use std::cmp::*; use std::collections::*; use std::io::Write; use std::ops::Bound::*; #[allow(unused_macros)] macro_rules! debug { ($($e:expr),*) => { #[cfg(debug_assertions)] $({ let (e, mut err) = (stringify!($e), std::io::stderr()); writeln!(err, "{} = {:?}", e, $e).unwrap() })* }; } fn main() { let n = read::(); let s = read::(); let v = read_vec::(); let mut poses = vec![vec![]; 4]; for (i, ch) in s.chars().enumerate() { match ch { 'y' => poses[0].push(i), 'u' => poses[1].push(i), 'k' => poses[2].push(i), _ => poses[3].push(i), } } let mut flow = MinCostFlowGraph::new(2 * n + 2); for i in 0..n { flow.add_edge(2 * i, 2 * i + 1, 1, 0); } for &pos in &poses[0] { flow.add_edge(2 * n, 2 * pos, 1, 0); } let inf: i64 = 1000000000; for ci in 0..3 { for i in 0..poses[ci].len() { let pi = poses[ci][i]; for j in 0..poses[ci + 1].len() { let pj = poses[ci + 1][j]; if pj < pi { continue; } flow.add_edge(2 * pi + 1, 2 * pj, 1, inf - v[pi]); } } } for &pos in &poses[3] { flow.add_edge(2 * pos + 1, 2 * n + 1, 1, inf - v[pos]); } flow.add_edge(2 * n, 2 * n + 1, n as i64 / 4, inf * 4); let (cap, cost) = flow.flow(2 * n, 2 * n + 1, n as i64 / 4); // debug!((cap, cost)); let ans = inf * cap * 4 - cost; println!("{}", ans); } fn read() -> T { let mut s = String::new(); std::io::stdin().read_line(&mut s).ok(); s.trim().parse().ok().unwrap() } fn read_vec() -> Vec { read::() .split_whitespace() .map(|e| e.parse().ok().unwrap()) .collect() } //https://github.com/rust-lang-ja/ac-library-rs pub mod internal_type_traits { use std::{ fmt, iter::{Product, Sum}, ops::{ Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign, Mul, MulAssign, Not, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign, }, }; // Skipped: // // - `is_signed_int_t` (probably won't be used directly in `modint.rs`) // - `is_unsigned_int_t` (probably won't be used directly in `modint.rs`) // - `to_unsigned_t` (not used in `fenwicktree.rs`) /// Corresponds to `std::is_integral` in C++. // We will remove unnecessary bounds later. // // Maybe we should rename this to `PrimitiveInteger` or something, as it probably won't be used in the // same way as the original ACL. pub trait Integral: 'static + Send + Sync + Copy + Ord + Not + Add + Sub + Mul + Div + Rem + AddAssign + SubAssign + MulAssign + DivAssign + RemAssign + Sum + Product + BitOr + BitAnd + BitXor + BitOrAssign + BitAndAssign + BitXorAssign + Shl + Shr + ShlAssign + ShrAssign + fmt::Display + fmt::Debug + fmt::Binary + fmt::Octal + Zero + One + BoundedBelow + BoundedAbove { } /// Class that has additive identity element pub trait Zero { /// The additive identity element fn zero() -> Self; } /// Class that has multiplicative identity element pub trait One { /// The multiplicative identity element fn one() -> Self; } pub trait BoundedBelow { fn min_value() -> Self; } pub trait BoundedAbove { fn max_value() -> Self; } macro_rules! impl_integral { ($($ty:ty),*) => { $( impl Zero for $ty { #[inline] fn zero() -> Self { 0 } } impl One for $ty { #[inline] fn one() -> Self { 1 } } impl BoundedBelow for $ty { #[inline] fn min_value() -> Self { Self::min_value() } } impl BoundedAbove for $ty { #[inline] fn max_value() -> Self { Self::max_value() } } impl Integral for $ty {} )* }; } impl_integral!(i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize); } pub mod mincostflow { use crate::internal_type_traits::Integral; pub struct MinCostFlowEdge { pub from: usize, pub to: usize, pub cap: T, pub flow: T, pub cost: T, } pub struct MinCostFlowGraph { pos: Vec<(usize, usize)>, g: Vec>>, cost_sum: T, } impl MinCostFlowGraph where T: Integral + std::ops::Neg, { pub fn new(n: usize) -> Self { Self { pos: vec![], g: (0..n).map(|_| vec![]).collect(), cost_sum: T::zero(), } } pub fn get_edge(&self, i: usize) -> MinCostFlowEdge { assert!(i < self.pos.len()); let e = &self.g[self.pos[i].0][self.pos[i].1]; let re = &self.g[e.to][e.rev]; MinCostFlowEdge { from: self.pos[i].0, to: e.to, cap: e.cap + re.cap, flow: re.cap, cost: e.cost, } } pub fn edges(&self) -> Vec> { let m = self.pos.len(); let mut result = vec![]; for i in 0..m { result.push(self.get_edge(i)); } result } pub fn add_edge(&mut self, from: usize, to: usize, cap: T, cost: T) -> usize { assert!(from < self.g.len()); assert!(to < self.g.len()); assert_ne!(from, to); assert!(cap >= T::zero()); assert!(cost >= T::zero()); self.pos.push((from, self.g[from].len())); self.cost_sum += cost; let rev = self.g[to].len(); self.g[from].push(_Edge { to, rev, cap, cost }); let rev = self.g[from].len() - 1; self.g[to].push(_Edge { to: from, rev, cap: T::zero(), cost: -cost, }); self.pos.len() - 1 } /// Returns (maximum flow, cost) pub fn flow(&mut self, source: usize, sink: usize, flow_limit: T) -> (T, T) { self.slope(source, sink, flow_limit).pop().unwrap() } pub fn slope(&mut self, source: usize, sink: usize, flow_limit: T) -> Vec<(T, T)> { let n = self.g.len(); assert!(source < n); assert!(sink < n); assert_ne!(source, sink); let mut dual = vec![T::zero(); n]; let mut prev_v = vec![0; n]; let mut prev_e = vec![0; n]; let mut flow = T::zero(); let mut cost = T::zero(); let mut prev_cost: Option = None; let mut result = vec![(flow, cost)]; while flow < flow_limit { if !self.refine_dual(source, sink, &mut dual, &mut prev_v, &mut prev_e) { break; } let mut c = flow_limit - flow; let mut v = sink; while v != source { c = std::cmp::min(c, self.g[prev_v[v]][prev_e[v]].cap); v = prev_v[v]; } let mut v = sink; while v != source { self.g[prev_v[v]][prev_e[v]].cap -= c; let rev = self.g[prev_v[v]][prev_e[v]].rev; self.g[v][rev].cap += c; v = prev_v[v]; } let d = -dual[source]; flow += c; cost += d * c; if prev_cost == Some(d) { assert!(result.pop().is_some()); } result.push((flow, cost)); prev_cost = Some(cost); } result } fn refine_dual( &self, source: usize, sink: usize, dual: &mut [T], pv: &mut [usize], pe: &mut [usize], ) -> bool { let n = self.g.len(); let mut dist = vec![self.cost_sum; n]; let mut vis = vec![false; n]; let mut que = std::collections::BinaryHeap::new(); dist[source] = T::zero(); que.push((std::cmp::Reverse(T::zero()), source)); while let Some((_, v)) = que.pop() { if vis[v] { continue; } vis[v] = true; if v == sink { break; } for (i, e) in self.g[v].iter().enumerate() { if vis[e.to] || e.cap == T::zero() { continue; } let cost = e.cost - dual[e.to] + dual[v]; if dist[e.to] - dist[v] > cost { dist[e.to] = dist[v] + cost; pv[e.to] = v; pe[e.to] = i; que.push((std::cmp::Reverse(dist[e.to]), e.to)); } } } if !vis[sink] { return false; } for v in 0..n { if !vis[v] { continue; } dual[v] -= dist[sink] - dist[v]; } true } } struct _Edge { to: usize, rev: usize, cap: T, cost: T, } #[cfg(test)] mod tests { use super::*; #[test] fn test_min_cost_flow() { let mut graph = MinCostFlowGraph::new(4); graph.add_edge(0, 1, 2, 1); graph.add_edge(0, 2, 1, 2); graph.add_edge(1, 2, 1, 1); graph.add_edge(1, 3, 1, 3); graph.add_edge(2, 3, 2, 1); let (flow, cost) = graph.flow(0, 3, 2); assert_eq!(flow, 2); assert_eq!(cost, 6); } } } use mincostflow::*;