use std::vec::Vec;
use std::cmp::*;

fn read<T: std::str::FromStr>() -> T {
    let mut s = String::new();
    std::io::stdin().read_line(&mut s).ok();
    s.trim().parse().ok().unwrap()
}

fn read_vec<T: std::str::FromStr>() -> Vec<T> {
    let mut s = String::new();
    std::io::stdin().read_line(&mut s).ok();
    s.trim().split_whitespace()
        .map(|e| e.parse().ok().unwrap()).collect()
}

fn main() {
    let n: usize = read();
    let h: Vec<i64> = read_vec();
    let a: Vec<i64> = read_vec();
    let mut dp = SegmentTree::new_max(&vec![0; n+1]);
    let mut ans = 0;
    for i in 0..n {
      let tmp = a[i] + dp.get_max(1, h[i] as usize + 1);
      ans = max(ans, tmp);
      dp.update_max(h[i] as usize, tmp);
    }

    println!("{}", ans);

}

#[derive(Debug, PartialEq)]
struct SegmentTree {
  n: usize, //元の配列に一番近い2の冪乗を格納する
  tree: Vec<i64>,
}


// 区間の最小の値を求めるセグメント木
impl SegmentTree {

  // 元の配列をセグメント木で表現
  // 2^nが元の配列のサイズ以上になる最小のnをNとする
  // O(N)
  fn new<F>(v: &Vec<i64>, init: i64, f: F) -> SegmentTree
    where F: Fn(i64, i64) -> i64 {
    let mut n: usize = 1;
    // セグメント木の葉の要素数
    while n < v.len() { n *= 2; }

    // セグメント木を表現するベクター
    let mut tree = vec![init; 2 * n - 1];  // 区間における最小値を求めるセグメント木に注意


    // 葉から順に値を格納していく
    // まず最下段に値を格納する
    for i in 0..v.len() { tree[i + n - 1] = v[i]; }

    // 最下段の次の下段から値を書くのしていく
    for i in (0..(n-1)).rev() {
      tree[i] = f(tree[2 * i + 1], tree[2 * i + 2]);  // 区間における最小値を求めるセグメント木に注意
    }

    SegmentTree { n: n, tree: tree }
  }


  // 元の配列をセグメント木で表現
  // 2^nが元の配列のサイズ以上になる最小のnをNとする
  // O(N)
  fn new_min(v: &Vec<i64>) -> SegmentTree {
    let mut n: usize = 1;
    // セグメント木の葉の要素数
    while n < v.len() { n *= 2; }

    // セグメント木を表現するベクター
    let mut tree = vec![std::i64::MAX; 2 * n - 1];  // 区間における最小値を求めるセグメント木に注意


    // 葉から順に値を格納していく
    // まず最下段に値を格納する
    for i in 0..v.len() { tree[i + n - 1] = v[i]; }

    // 最下段の次の下段から値を書くのしていく
    for i in (0..(n-1)).rev() {
      tree[i] = min(tree[2 * i + 1], tree[2 * i + 2]);  // 区間における最小値を求めるセグメント木に注意
    }

    SegmentTree { n: n, tree: tree }
  }


  // 元の配列をセグメント木で表現
  // 2^nが元の配列のサイズ以上になる最小のnをNとする
  // O(N)
  fn new_max(v: &Vec<i64>) -> SegmentTree {
    let mut n: usize = 1;
    // セグメント木の葉の要素数
    while n < v.len() { n *= 2; }

    // セグメント木を表現するベクター
    let mut tree = vec![std::i64::MIN; 2 * n - 1];  // 区間における最小値を求めるセグメント木に注意


    // 葉から順に値を格納していく
    // まず最下段に値を格納する
    for i in 0..v.len() { tree[i + n - 1] = v[i]; }

    // 最下段の次の下段から値を書くのしていく
    for i in (0..(n-1)).rev() {
      tree[i] = max(tree[2 * i + 1], tree[2 * i + 2]);  // 区間における最小値を求めるセグメント木に注意
    }

    SegmentTree { n: n, tree: tree }
  }


  // 元の配列のi番目の値が変更された時にセグメント木を更新
  // O(logN) Nは元の配列の長さを超える最小の2の冪乗
  fn update<F>(&mut self, i: usize, val: i64, f: F)
    where
      F: Fn(i64, i64) -> i64,
  {
    // 最下段の葉を更新
    let mut m = i + self.n - 1;
    self.tree[m] = val;
    while m > 0 {
      m = (m - 1) / 2;
      self.tree[m] = f(self.tree[2 * m + 1], self.tree[2 * m + 2]); // 区間における最小値を求めるセグメント木に注意
    }
  }


  // 元の配列のi番目の値が変更された時にセグメント木を更新
  // O(logN) Nは元の配列の長さを超える最小の2の冪乗
  fn update_min(&mut self, i: usize, val: i64) {
    // 最下段の葉を更新
    let mut m = i + self.n - 1;
    self.tree[m] = val;
    while m > 0 {
      m = (m - 1) / 2;
      self.tree[m] = min(self.tree[2 * m + 1], self.tree[2 * m + 2]); // 区間における最小値を求めるセグメント木に注意
    }
  }


  // 元の配列のi番目の値が変更された時にセグメント木を更新
  // O(logN) Nは元の配列の長さを超える最小の2の冪乗
  fn update_max(&mut self, i: usize, val: i64) {
    // 最下段の葉を更新
    let mut m = i + self.n - 1;
    self.tree[m] = val;
    while m > 0 {
      m = (m - 1) / 2;
      self.tree[m] = max(self.tree[2 * m + 1], self.tree[2 * m + 2]); // 区間における最小値を求めるセグメント木に注意
    }
  }



  // 指定された区間の最小値を求める
  // 区間は半開区間[i, j)での最小値を求める
  // O(logN)
  fn get_min(&self, i: usize, j: usize) -> i64 {
    fn get_min_sub(t: &SegmentTree, a: usize, b: usize, k: usize, l: usize, r: usize) -> i64 {
      // 求めたい区間が今いるノードの対応する区間と交差していない
      if r <= a || b <= l { return std::i64::MAX; } // 区間における最小値を求めるセグメント木に注意

      // 求めたい区間が今いるノードの対応する区間を被覆してる
      if a <= l && r <= b { return t.tree[k]; }

      // 求めたい区間と今いるノードと対応している区間が交差してるとき
      // 子を探索する
      // 左の子の値
      let vl = get_min_sub(t, a, b, 2 * k + 1, l, (l + r) / 2);
      // 右のこの値
      let vr = get_min_sub(t, a, b, 2 * k + 2, (l + r) / 2, r);

      return min(vl, vr);
    }

    return get_min_sub(&self, i, j, 0, 0, self.n);
  }


  // 指定された区間の最大値を求める
  // 区間は半開区間[i, j)での最小値を求める
  // O(logN)
  fn get_max(&self, i: usize, j: usize) -> i64 {
    fn get_max_sub(t: &SegmentTree, a: usize, b: usize, k: usize, l: usize, r: usize) -> i64 {
      // 求めたい区間が今いるノードの対応する区間と交差していない
      if r <= a || b <= l { return std::i64::MIN; } // 区間における最大値を求めるセグメント木に注意

      // 求めたい区間が今いるノードの対応する区間を被覆してる
      if a <= l && r <= b { return t.tree[k]; }

      // 求めたい区間と今いるノードと対応している区間が交差してるとき
      // 子を探索する
      // 左の子の値
      let vl = get_max_sub(t, a, b, 2 * k + 1, l, (l + r) / 2);
      // 右のこの値
      let vr = get_max_sub(t, a, b, 2 * k + 2, (l + r) / 2, r);

      return max(vl, vr);
    }

    return get_max_sub(&self, i, j, 0, 0, self.n);
  }

}



Measured memory_bytes: 13556