#![allow(non_snake_case,unused_imports,dead_code)]
use std::io::Read;
use std::cmp::max;
use std::cmp::min;
use std::str::FromStr;
use std::collections::HashMap;
use std::collections::HashSet;
use std::collections::BinaryHeap;
use std::cmp::Ordering;

fn comp_f64(a: &f64, b: &f64) -> Ordering {
    if a < b {
        return Ordering::Less;
    } else if a > b {
        return Ordering::Greater;
    }
    Ordering::Equal
}

fn main() {

    let mut buf = String::new();
    std::io::stdin().read_to_string(&mut buf).unwrap();
    let mut buf_it = buf.split_whitespace();

    let N = buf_it.next().unwrap().parse::<usize>().unwrap();
    
    let XD = (0..N).map(|_|
        (buf_it.next().unwrap().parse::<isize>().unwrap(),
        buf_it.next().unwrap().parse::<isize>().unwrap())
    ).collect::<Vec<_>>();

    let mut LR = XD.iter().map(|xd| (xd.0,xd.0+xd.1)).collect::<Vec<_>>();
    LR.sort();
    let x_min = LR[0].0-1;

    let mut R = SegmentTree::new(N,max,x_min);
    R.set(N-1,LR[N-1].1);
    for i in 0..N-1{
        let i = N-2 - i;
        let mut r = LR[i].1;
        let j = {
            if r>LR[N-1].0{
                N
            } else {
                let mut la = i;
                let mut ra = N;
                while la+1<ra{
                    let k = (la+ra)/2;
                    if LR[k].0 < r{
                        la = k;
                    } else {
                        ra = k;
                    }
                }
                ra
            }
        };
        r = max(r,R.get(i,j));
        R.set(i,r);
    }

    let get = |n| {
        let mut la = 0;
        let mut ra = N;
        while la+1<ra{
            let i = (la+ra)/2;
            if LR[i].0 < n{
                la = i;
            } else {
                ra = i;
            }
        }
        ra
    };

    let MOD:isize = 998244353;
    let mut dp = (0..N+1).map(|_| 1).collect::<Vec<isize>>();
    for i in 0..N{
        let i = N-1 - i;
        let r = R.get(i,i+1);
        dp[i] = dp[i+1] + dp[get(r)];
        dp[i] %= MOD;
    }
    println!("{}",dp[0]);


}

struct SegmentTree<N,F>
where F:Fn(N,N) ->N{
    n: usize,
    T: Vec<N>,
    func: F,
    identity: N,
}

impl<N:Copy,F> SegmentTree<N,F>
where F:Fn(N,N) ->N {
    fn new(n:usize,func:F,identity:N) -> SegmentTree<N,F>{
        let mut T = SegmentTree { n:n,T:Vec::new(),func:func,identity:identity};
        T.init();
        T
    }
    fn init(&mut self){
        self.n = 1<<format!("{:b}",self.n-1).len();
        self.T = (0..(2*self.n-1)).map(|_| self.identity).collect();
    }
    fn set(&mut self,index:usize,value:N){
        let i = index + self.n - 1;
        self.T[i] = value;
        self._update_parent((i-1)/2);
    }
    fn _update_parent(&mut self,i:usize){
        self.T[i] = (self.func)(self.T[i*2+1],self.T[i*2+2]);
        if i>0 { self._update_parent((i-1)/2)}
    }
    fn get(&self,l:usize,r:usize) -> N{
    self._get(l,r,0,0,self.n)
    }
    fn _get(&self,l:usize,r:usize,i:usize,a:usize,b:usize) -> N{
        if l<=a && b<=r {
            self.T[i]
        } else if b<=l || r<=a {
            self.identity
        } else{
            (self.func)(self._get(l,r,2*i+1,a,(a+b)/2),
                        self._get(l,r,2*i+2,(a+b)/2,b))
        }
    }
}

Measured memory_bytes: 23800