// use rand::{thread_rng, Rng};

const MOD: usize = 998244353;
fn main() {
    let (r, w) = (std::io::stdin(), std::io::stdout());
    let mut sc = IO::new(r.lock(), w.lock());

    // loop {
    //     let n = 4;
    //     let mut robots = vec![];
    //     let mut rng = thread_rng();
    //     for _ in 0..n {
    //         let x = rng.gen::<u8>() as i64;
    //         let d = rng.gen::<u8>() as i64 + 1;
    //         robots.push((x, d));
    //     }
    //     robots.sort_by_key(|r| r.0);
    //
    //     let ans = solve(&robots);
    //     let naive = naive(&robots);
    //     assert_eq!(ans, naive, "ans:{} naive:{} {:?}", ans, naive, robots);
    // }

    let n: usize = sc.read();
    let mut robots: Vec<(i64, i64)> = (0..n).map(|_| (sc.read(), sc.read())).collect();
    robots.sort_by_key(|r| r.0);
    let ans = solve(&robots);
    // let naive = naive(&robots);
    // assert_eq!(ans, naive, "ans:{} naive:{} {:?}", ans, naive, robots);
    println!("{}", ans);
}

fn solve(robots: &Vec<(i64, i64)>) -> usize {
    let n = robots.len();
    let mut dp = vec![[0, 0]; n];
    let mut seg = SegmentTree::new(n, 0, std::cmp::max);
    seg.update(n - 1, n - 1);
    dp[n - 1][0] = 1;
    dp[n - 1][1] = 1;
    for i in (0..(n - 1)).rev() {
        let touch = robots[i].0 + robots[i].1;
        let upper = robots
            .binary_search_by_key(&(touch * 2 - 1), |r| r.0 * 2)
            .err()
            .unwrap();
        assert!(upper == n || touch <= robots[upper].0);
        dp[i][0] = (dp[i + 1][0] + dp[i + 1][1]) % MOD;

        if upper <= i + 1 {
            dp[i][1] = (dp[i + 1][0] + dp[i + 1][1]) % MOD;
            seg.update(i, i);
        } else {
            let next = seg.query(i + 1, upper);
            seg.update(i, next);
            dp[i][1] = dp[next][1];
        }
    }
    (dp[0][0] + dp[0][1]) % MOD
}

fn naive(robots: &Vec<(i64, i64)>) -> usize {
    let n = robots.len();
    let mut set = std::collections::BTreeSet::new();
    for mask in 0..(1 << n) {
        let mut dead = 0;
        let mut cur_min = std::i64::MIN;
        for i in 0..n {
            if mask & (1 << i) != 0 || robots[i].0 < cur_min {
                cur_min = std::cmp::max(cur_min, robots[i].0 + robots[i].1);
            }
            if robots[i].0 < cur_min {
                dead += 1 << i;
            }
        }
        set.insert(dead);
    }
    set.len()
}
pub struct SegmentTree<T, F> {
    seg: Vec<T>,
    n: usize,
    f: F,
    initial_value: T,
}

impl<T: Copy, F> SegmentTree<T, F>
where
    F: Fn(T, T) -> T,
{
    pub fn new(size: usize, initial_value: T, f: F) -> SegmentTree<T, F> {
        let mut m = 1;
        while m <= size {
            m <<= 1;
        }
        SegmentTree {
            seg: vec![initial_value; m * 2],
            n: m,
            f: f,
            initial_value: initial_value,
        }
    }

    pub fn update(&mut self, k: usize, value: T) {
        let mut k = k;
        k += self.n - 1;
        self.seg[k] = value;
        while k > 0 {
            k = (k - 1) >> 1;
            self.seg[k] = (self.f)(self.seg[k * 2 + 1], self.seg[k * 2 + 2]);
        }
    }

    /// Get the minimum value in the array in the range [a, b)
    ///
    /// # Panics
    ///
    /// Panics if `a >= b`.
    pub fn query(&self, a: usize, b: usize) -> T {
        assert!(a < b);
        self.query_range(a, b, 0, 0, self.n)
    }

    fn query_range(&self, a: usize, b: usize, k: usize, l: usize, r: usize) -> T {
        if r <= a || b <= l {
            self.initial_value
        } else if a <= l && r <= b {
            self.seg[k]
        } else {
            let x = self.query_range(a, b, k * 2 + 1, l, (l + r) >> 1);
            let y = self.query_range(a, b, k * 2 + 2, (l + r) >> 1, r);
            (self.f)(x, y)
        }
    }
}
pub struct IO<R, W: std::io::Write>(R, std::io::BufWriter<W>);

impl<R: std::io::Read, W: std::io::Write> IO<R, W> {
    pub fn new(r: R, w: W) -> Self {
        IO(r, std::io::BufWriter::new(w))
    }
    pub fn write<S: ToString>(&mut self, s: S) {
        use std::io::Write;
        self.1.write(s.to_string().as_bytes()).unwrap();
    }
    pub fn read<T: std::str::FromStr>(&mut self) -> T {
        use std::io::Read;
        let buf = self
            .0
            .by_ref()
            .bytes()
            .map(|b| b.unwrap())
            .skip_while(|&b| b == b' ' || b == b'\n' || b == b'\r' || b == b'\t')
            .take_while(|&b| b != b' ' && b != b'\n' && b != b'\r' && b != b'\t')
            .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()
    }
}


Measured memory_bytes: 18684