#include <bits/stdc++.h>

#define debug(s) cout << s << endl
#define debug1(s, t) cout << s << " " << t << endl
#define debug2(s, t, u) cout << s << " " << t << " " << u << endl
#define printvec(v)       \
    for (auto &&i : v)    \
        cout << i << " "; \
    cout << endl

using namespace std;
using i64 = long long;

const int P = 998244353;

template <class M> // M: モノイド
struct SegmentTree
{
    vector<M> tree;
    int size;     // ベクトル全体の長さ
    int leaf_len; // 葉（区間）の長さ
    M mempty;
    function<M(M, M)> binary_operation;

    // コンストラクタ nは2の累乗（葉の個数） eは単位元
    SegmentTree(int n, M e, function<M(M, M)> biop)
        : tree(2 * n - 1, e),
          size(2 * n - 1),
          leaf_len(n),
          mempty(e),
          binary_operation(biop)
    {
    }
    // コンストラクタ nは2の累乗（葉の個数）e: 単位元 initval: 初期値
    SegmentTree(int n, M e, function<M(M, M)> biop, M initval)
        : tree(2 * n - 1, initval),
          size(2 * n - 1),
          leaf_len(n),
          mempty(e),
          binary_operation(biop)
    {
    }
    // コンストラクタ nは2の累乗（葉の個数）e: 単位元 initvec: 初期値のベクトル
    SegmentTree(int n, M e, function<M(M, M)> biop, vector<M> initvec)
        : tree(2 * n - 1, e),
          size(2 * n - 1),
          leaf_len(n),
          mempty(e),
          binary_operation(biop)
    {
        for (int k = n - 1; k <= 2 * n - 2; k++)
            tree[k] = initvec[k + 1 - n];
        for (int k = n - 2; k >= 0; k--)
            tree[k] = biop(lchild(k), rchild(k));
    }

    // 葉の座標、値を格納
    void set(int i_leaf, M x) { tree[to_tree_ix(i_leaf)] = x; }

    // まとめて更新
    void build()
    {
        for (int k = leaf_len - 2; k >= 0; k--)
            tree[k] = binary_operation(lchild(k), rchild(k));
    }

    // 値を返すのみ、参照ではない
    M operator[](int i_leaf) { return tree[to_tree_ix(i_leaf)]; }

private:
    // 子、親への参照　左辺値にもなれる
    M &lchild(int ix) { return tree[2 * ix + 1]; }
    M &rchild(int ix) { return tree[2 * ix + 2]; }
    M &par(int ix) { return tree[(ix - 1) / 2]; }

    constexpr int to_tree_ix(int i_leaf) { return i_leaf + leaf_len - 1; }
    constexpr int get_lchild_ix(int ix) { return 2 * ix + 1; }
    constexpr int get_rchild_ix(int ix) { return 2 * ix + 2; }
    constexpr int get_par_ix(int ix) { return (ix - 1) / 2; }

public:
    // 葉の値の更新 (O(log n))
    void update(int ix, M x)
    {
        set(ix, x);
        ix = to_tree_ix(ix);
        while (ix > 0)
        {
            ix = get_par_ix(ix);
            tree[ix] = binary_operation(lchild(ix), rchild(ix));
        }
    }

    // 半開区間中を二項演算で畳み込む
    M fold(int a, int b) { return fold_sub(a, b, 0, 0, leaf_len); }

private:
    M fold_sub(int a, int b, int k, int l, int r)
    {
        // k: 現在見ているノードの位置
        // [l,r): tree[k] が表している区間
        if (r <= a || b <= l)
            return mempty;
        else if (a <= l && r <= b)
            return tree[k];
        else
        {
            M vl = fold_sub(a, b, get_lchild_ix(k), l, (l + r) / 2);
            M vr = fold_sub(a, b, get_rchild_ix(k), (l + r) / 2, r);
            return binary_operation(vl, vr);
        }
    }

public:
    void print()
    {
        int p = 0;
        int nn = size;
        while (nn != 1)
        {
            nn /= 2;
            p++;
        }
        for (int i = 0; i <= p; i++)
        {
            for (int j = 0; j < pow(2, i); j++)
            {
                cout << tree[pow(2, i) - 1 + j] << " ";
            }
            cout << endl;
        }
    }
};

int smallest_2power_largerthan(int n)
{
    int twopower = 1;
    do
    {
        twopower *= 2;
    } while (twopower < n);
    return twopower;
}

int main()
{
    int n;
    cin >> n;

    vector<pair<int, int>> xd(n);

    for (auto &&item : xd)
    {
        cin >> item.first >> item.second;
    }

    sort(xd.begin(), xd.end(), [](pair<int, int> lhs, pair<int, int> rhs) {
        return lhs.first < rhs.first;
    });

    // 対象Ri、演算maxのsegment-tree
    int N = smallest_2power_largerthan(n);
    auto biop = [](int a, int b) { return max(a, b); };
    SegmentTree<int> st(N, 0, biop);
    st.update(n - 1, n - 1);

    auto p = [&xd](int j, int i) { return xd[j].first < xd[i].first + xd[i].second; };
    for (int i = n - 2; i >= 0; i--)
    {
        if (!p(i + 1, i))
        {
            st.update(i, i);
            continue;
        }

        // 二分探索 lが答え
        int l = i;
        int r = n;
        while (r - l > 1)
        {
            int mid = (l + r) / 2;
            if (p(mid, i))
                l = mid;
            else
                r = mid;
        }

        // [i+1, l+1) の最大値
        int ri = st.fold(i + 1, l + 1);
        // debug2(xd[i].first, l, ri);
        st.update(i, ri);
    }
    // ここまででRi計算完了

    // DPi: ロボットi以降のみを考慮した場合の組み合わせの個数
    // DPi = DP(i+1) + DP(Ri+1)

    vector<i64> dp(n);
    dp[n - 1] = 2;
    for (int i = n - 2; i >= 0; i--)
    {
        if (st[i] + 1 >= n)
        {
            dp[i] = dp[i + 1] + 1;
        }
        else
        {
            dp[i] = dp[i + 1] + dp[st[i] + 1];
        }
        dp[i] %= P;
    }

    // st.print();

    /*
    for (int i = 0; i < n; i++)
    {
        debug1(i, st[i]);
    }
    */
    cout << dp[0] << endl;

    return 0;
}

Measured memory_bytes: 5376