#include<bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<vector>
#include<stdio.h>
#include<cstring>
#include<math.h>
#include<map>
#include<bitset>
#include<iomanip>
#include<queue>
#include<functional>
#include<stack>
#include<tuple>
#include<cassert>
using namespace std;

#define rep(i, n) for (long long i = 0; i < (long long)(n); ++i)
using ll = long long;
using P = pair<long long, long long>;
using MATRIX = vector< vector<ll> >;
template <class T> inline bool chmin(T &x, const T &y) {if (x > y) {x = y; return 1;} return 0;}
template <class T> inline bool chmax(T &x, const T &y) {if (x < y) {x = y; return 1;} return 0;}

const long long INF = 1000000000;
const long long MOD = 998244353;

/* RSQ：[0,n-1] について、区間ごとの合計を管理する構造体
    update(i,x): i 番目の要素を x に更新。O(log(n))
    query(a,b): [a,b) での合計を取得。O(log(n))
*/
template <typename T>
struct RSQ {
    //const T INF = numeric_limits<T>::max();
    long long n;         // 葉の数
    vector<T> dat, lazy; // 完全二分木の配列
    RSQ(long long n_) : n(), dat(n_ * 4, 0), lazy(n_*4, 0) { // 葉の数は 2^x の形
        long long x = 1;
        while (n_ > x) {
            x *= 2;
        }
        n = x;
    }
 
    void update(long long i, T x) {
        i += n - 1;
        dat[i] = x;
        while (i > 0) {
            i = (i - 1) / 2;  // parent
            dat[i] = dat[i * 2 + 1] + dat[i * 2 + 2];
            dat[i] %= MOD;
        }
    }
 
    // the minimum element of [a,b)
    T query(long long a, long long b) { return query_sub(a, b, 0, 0, n); }
    T query_sub(long long a, long long b, long long k, long long l, long long r) {
        eval(k, l, r);
        if (r <= a || b <= l) {
            return 0;
        } else if (a <= l && r <= b) {
            return dat[k];
        } else {
            T vl = query_sub(a, b, k * 2 + 1, l, (l + r) / 2);
            T vr = query_sub(a, b, k * 2 + 2, (l + r) / 2, r);
            return (vl + vr) % MOD;
        }
    }

    void eval(long long k, long long l, long long r) {
        if (lazy[k] == 0) return;  // 更新するものが無ければ終了
        if (k < n - 1) {             // 葉でなければ子に伝搬
            lazy[k * 2 + 1] = lazy[k];
            lazy[k * 2 + 2] = lazy[k];
        }
        // 自身を更新
        dat[k] += (lazy[k] * (r - l)) % MOD;
        dat[k] %= MOD;
        lazy[k] = 0;
    }
    void update(long long a, long long b, T x, long long k, long long l, long long r) {
        eval(k, l, r);
        if (a <= l && r <= b) {  // 完全に内側の時
            lazy[k] = x;
            eval(k, l, r);
        } else if (a < r && l < b) {                     // 一部区間が被る時
            update(a, b, x, k * 2 + 1, l, (l + r) / 2);  // 左の子
            update(a, b, x, k * 2 + 2, (l + r) / 2, r);  // 右の子
            dat[k] = dat[k * 2 + 1] + dat[k * 2 + 2];
        }
    }
    void update(long long a, long long b, T x) { update(a, b, x, 0, 0, n); }
};

int main() {
  long long N, K;
  cin >> N >> K;
  RSQ<long long> tree(N);
  tree.update(0, 1);
  vector<P> query;
  rep(i, K) {
    long long l, r;
    cin >> l >> r;
    query.push_back(make_pair(l, r));
  }
  for(long long i = 1; i <= N-1; i++) {
    long long temp = 0;
    rep(j, K) {
      long long l = query[j].first;
      long long r = query[j].second;
      temp += tree.query(i-r, i-l+1);
      temp %= MOD;
    }
    // cout << temp << endl;
    tree.update(i, temp);
  }
  cout << tree.query(N-1, N) << endl;
  return 0;
}

Measured memory_bytes: 15604