use proconio::input;
use proconio::marker::Chars;
#[allow(unused_imports)]
use std::cmp::{max, min};
#[allow(unused)]
const ALPHA_SMALL: [char; 26] = [
    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
    't', 'u', 'v', 'w', 'x', 'y', 'z',
];
#[allow(unused)]
const ALPHA: [char; 26] = [
    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
    'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
];
macro_rules! maxs {
    ($x:expr, $($y:expr), *) => {{
        let mut ret = $x;
        $(
            ret = max(ret, $y);
        )*
        ret
    }};
}

// const MOD: usize = 998244353;

// fn main() {
//     input!(N: usize, K: usize);
//     let mut lrs = vec![];
//     for _ in 0..K {
//         input!(t: (usize, usize));
//         lrs.push(t);
//     }
//     let mut sum = vec![0; N + 2];
//     let mut dp = vec![0; N + 1];
//     // init
//     dp[0] = 0;
//     dp[1] = 1;

//     for i in 0..N + 1 {
//         for j in 0..K {
//             let (l, r) = lrs[j];
//             if i < l {
//                 continue;
//             }
//             let (l, r) = (l, min(i, r));
//             let s = sum[i - l + 1] + MOD - sum[i - r];
//             dp[i] += s;
//             dp[i] %= MOD;
//         }

//         sum[i + 1] = sum[i] + dp[i];
//         sum[i + 1] %= MOD;
//     }
//     println!("{}", dp[N]);
// }
const MOD: usize = 998244353;
fn main() {
    input!(N: usize, K: usize);
    let mut lrs = vec![];
    for _ in 0..K {
        input!(t: (usize, usize));
        lrs.push(t);
    }
    let mut dp = vec![0; N + 1];
    let mut lst = LazySegmentTree::new(&dp);

    lst.update(1, 2, &1);
    for i in 1..N + 1 {
        let val = lst.get(i, i + 1);
        for &(l, r) in &lrs {
            if i + l <= N {
                let (l, r) = (i + l, min(i + r, N) + 1);
                lst.update(l, r, &val);
            }
        }
    }
    //println!("{:?}", lst);
    let ans = lst.get(N, N + 1);
    println!("{}", ans);
}

impl Monoid for usize {
    const E: usize = 0;
    fn op(&self, rhs: &usize) -> Self {
        self + rhs
    }
}

impl Update<usize> for usize {
    fn up(&self, val: &usize) -> Self {
        (self + val) % MOD
    }
    fn pow(&self, len: usize) -> Self {
        (self * len) % MOD
    }
}

#[allow(clippy::declare_interior_mutable_const)]
pub trait Monoid<Rhs = Self> {
    const E: Rhs;
    fn op(&self, other: &Rhs) -> Self;
}

pub trait Update<Rhs = Self> {
    fn up(&self, val: &Rhs) -> Self;
    fn pow(&self, len: usize) -> Self;
}

#[derive(Debug, PartialEq, Clone)]
pub struct LazySegmentTree<T: Clone + Monoid + PartialEq + Update> {
    n: usize, //元の配列に一番近い2の冪乗を格納する
    tree: Vec<T>,
    lazy: Vec<T>,
}

// 区間の最小の値を求めるセグメント木
impl<T: Clone + Monoid + PartialEq + Update + std::fmt::Debug> LazySegmentTree<T> {
    // 元の配列をセグメント木で表現
    // 2^nが元の配列のサイズ以上になる最小のnをNとする
    // O(N)
    pub fn new(v: &[T]) -> Self {
        let mut n: usize = 1;
        // セグメント木の葉の要素数
        while n < v.len() {
            n *= 2;
        }

        // セグメント木を表現するベクター
        let mut tree = vec![T::E; 2 * n - 1];
        // 遅延評価用の配列lazy
        let lazy = vec![T::E; 2 * n - 1];

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

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

        LazySegmentTree { n, tree, lazy }
    }

    // 遅延評価をする関数
    fn eval(&mut self, k: usize, l: usize, r: usize) {
        let e = T::E;
        // 更新する物がなければ終了
        if self.lazy[k] == e {
            return;
        }
        if k < self.n - 1 {
            self.lazy[k * 2 + 1] = T::op(&self.lazy[k * 2 + 1], &self.lazy[k]);
            self.lazy[k * 2 + 2] = T::op(&self.lazy[k * 2 + 2], &self.lazy[k]);
        }
        self.tree[k] = T::up(&self.tree[k], &T::pow(&self.lazy[k], r - l));
        self.lazy[k] = e;
    }

    pub fn update(&mut self, i: usize, j: usize, val: &T) {
        fn update_sub<T: Clone + Monoid + PartialEq + Update + std::fmt::Debug>(
            t: &mut LazySegmentTree<T>,
            a: usize,
            b: usize,
            val: &T,
            k: usize,
            l: usize,
            r: usize,
        ) {
            t.eval(k, l, r);
            if a <= l && r <= b {
                t.lazy[k] = T::up(&t.lazy[k], val);
                t.eval(k, l, r);
            } else if a < r && l < b {
                update_sub(t, a, b, val, k * 2 + 1, l, (l + r) / 2);
                update_sub(t, a, b, val, k * 2 + 2, (l + r) / 2, r);
                t.tree[k] = T::op(&t.tree[k * 2 + 1], &t.tree[k * 2 + 2]);
            }
        }

        update_sub(self, i, j, val, 0, 0, self.n)
    }

    pub fn get(&mut self, i: usize, j: usize) -> T {
        fn get_sub<T: Clone + Monoid + PartialEq + Update + std::fmt::Debug>(
            t: &mut LazySegmentTree<T>,
            a: usize,
            b: usize,
            k: usize,
            l: usize,
            r: usize,
        ) -> T {
            t.eval(k, l, r);
            // 求めたい区間が今いるノードの対応する区間と交差していない
            if r <= a || b <= l {
                return T::E;
            } // 区間における最小値を求めるセグメント木に注意

            // 求めたい区間が今いるノードの対応する区間を被覆してる
            if a <= l && r <= b {
                //t.lazy[k] = T::update(&t.lazy[k], &val);

                return t.tree[k].clone();
            }

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

            T::op(&vl, &vr)
        }

        get_sub(self, i, j, 0, 0, self.n)
    }
}


Measured memory_bytes: 8792