Problem:
Score : 600 points

Problem Statement

There are N robots numbered 1 to N placed on a number line. Robot i is placed at coordinate X_i. When activated, it will travel the distance of D_i in the positive direction, and then it will be removed from the number line. All the robots move at the same speed, and their sizes are ignorable.

Takahashi, who is a mischievous boy, can do the following operation any number of times (possibly zero) as long as there is a robot remaining on the number line.

Choose a robot and activate it. This operation cannot be done when there is a robot moving.

While Robot i is moving, if it touches another robot j that is remaining in the range [X_i, X_i + D_i) on the number line, Robot j also gets activated and starts moving. This process is repeated recursively.

How many possible sets of robots remaining on the number line are there after Takahashi does the operation some number of times? Compute this count modulo 998244353, since it can be enormous.

Constraints

1 \leq N \leq 2 \times 10^5

-10^9 \leq X_i \leq 10^9

1 \leq D_i \leq 10^9

X_i \neq X_j (i \neq j)

All values in input are integers.

Input

Input is given from Standard Input in the following format:

N
X_1 D_1
:
X_N D_N

Output

Print the number of possible sets of robots remaining on the number line, modulo 998244353.

Sample Input 1

2
1 5
3 3

Sample Output 1

3

There are three possible sets of robots remaining on the number line: \{1, 2\}, \{1\}, and \{\}.

These can be achieved as follows:

If Takahashi activates nothing, the robots \{1, 2\} will remain.

If Takahashi activates Robot 1, it will activate Robot 2 while moving, after which there will be no robots on the number line. This state can also be reached by activating Robot 2 and then Robot 1.

If Takahashi activates Robot 2 and finishes doing the operation, the robot \{1\} will remain.

Sample Input 2

3
6 5
-1 10
3 3

Sample Output 2

5

There are five possible sets of robots remaining on the number line: \{1, 2, 3\}, \{1, 2\}, \{2\}, \{2, 3\}, and \{\}.

Sample Input 3

4
7 10
-10 3
4 3
-4 3

Sample Output 3

16

None of the robots influences others.

Sample Input 4

20
-8 1
26 4
0 5
9 1
19 4
22 20
28 27
11 8
-3 20
-25 17
10 4
-18 27
24 28
-11 19
2 27
-2 18
-1 12
-24 29
31 29
29 7

Sample Output 4

110

Remember to print the count modulo 998244353.

Solution in C# (Mono 4.6.2.0) (judge verdict: Accepted)

#region いつもの
using System;
using System.Collections.Generic;
using System.Linq;
using IEnumerable = System.Collections.IEnumerable;
using IEnumerator = System.Collections.IEnumerator;
using BigInteger = System.Numerics.BigInteger;
using TextReader = System.IO.TextReader;
using static Global;
using static NumGlobal;

static class Global
{
    public static T[] NewArray<T>(int len0, T value) => new T[len0].Fill(value);
    public static T[] NewArray<T>(int len0, Func<T> factory)
    {
        var arr = new T[len0];
        for (int i = 0; i < arr.Length; i++) arr[i] = factory();
        return arr;
    }
    public static T[][] NewArray<T>(int len0, int len1, T value) where T : struct
    {
        var arr = new T[len0][];
        for (int i = 0; i < arr.Length; i++) arr[i] = NewArray(len1, value);
        return arr;
    }
    public static T[][] NewArray<T>(int len0, int len1, Func<T> factory)
    {
        var arr = new T[len0][];
        for (int i = 0; i < arr.Length; i++) arr[i] = NewArray(len1, factory);
        return arr;
    }
    public static T[][][] NewArray<T>(int len0, int len1, int len2, T value) where T : struct
    {
        var arr = new T[len0][][];
        for (int i = 0; i < arr.Length; i++) arr[i] = NewArray(len1, len2, value);
        return arr;
    }
    public static T[][][] NewArray<T>(int len0, int len1, int len2, Func<T> factory)
    {
        var arr = new T[len0][][];
        for (int i = 0; i < arr.Length; i++) arr[i] = NewArray(len1, len2, factory);
        return arr;
    }
    public static T[][][][] NewArray<T>(int len0, int len1, int len2, int len3, T value) where T : struct
    {
        var arr = new T[len0][][][];
        for (int i = 0; i < arr.Length; i++) arr[i] = NewArray(len1, len2, len3, value);
        return arr;
    }
    public static T[][][][] NewArray<T>(int len0, int len1, int len2, int len3, Func<T> factory)
    {
        var arr = new T[len0][][][];
        for (int i = 0; i < arr.Length; i++) arr[i] = NewArray(len1, len2, len3, factory);
        return arr;
    }
    private class ComparerReverseImpl<T> : Comparer<T> where T : IComparable<T> { public override int Compare(T y, T x) => x.CompareTo(y); public override bool Equals(object obj) => obj != null && GetType() == obj.GetType(); public override int GetHashCode() => GetType().GetHashCode(); }
    public static IComparer<T> ComparerReverse<T>() where T : IComparable<T> => new ComparerReverseImpl<T>();
    public static string AllLines<T>(IEnumerable<T> source) => string.Join("\n", source);
    public static string AllJoin<T>(IEnumerable<T> source) => string.Join(" ", source);
}
static class NumGlobal
{
    public static int Pow(int x, int y)
    {
        int res = 1;
        for (; y > 0; y >>= 1)
        {
            if ((y & 1) == 1) res *= x;
            x *= x;
        }
        return res;
    }
    public static BigInteger ParseBigInteger(string s)
    {
        // MonoのBigInteger.Parseが遅いので自前実装
        var res = BigInteger.Zero;
        var splited = new string[(s.Length + 7) / 8];
        for (var i = 0; i < splited.Length - 1; i++)
        {
            splited[i] = s.Substring(8 * i, 8);
        }
        splited[splited.Length - 1] = s.Substring(8 * (splited.Length - 1));
        foreach (var sp in splited)
        {
            res *= (int)Math.Pow(10, sp.Length);
            res += int.Parse(sp);
        }
        return res;
    }
    public static int BitCount(int x) { x -= (x >> 1) & 0x55555555; x = (x & 0x33333333) + ((x >> 2) & 0x33333333); x = (x + (x >> 4)) & 0x0f0f0f0f; x += x >> 8; x += x >> 16; return x & 0x3f; }
    public static int BitCount(long x) { x -= (x >> 1) & 0x5555555555555555; x = (x & 0x3333333333333333) + ((x >> 2) & 0x3333333333333333); x = (x + (x >> 4)) & 0x0f0f0f0f0f0f0f0f; x += x >> 8; x += x >> 16; x += x >> 32; return (int)(x & 0x0000007f); }
    public static int MSB(int x) { x |= x >> 1; x |= x >> 2; x |= x >> 4; x |= x >> 8; x |= x >> 16; return BitCount(x) - 1; }
    public static int MSB(long x) { x |= x >> 1; x |= x >> 2; x |= x >> 4; x |= x >> 8; x |= x >> 16; x |= x >> 32; return BitCount(x) - 1; }
    public static int LSB(int x) { x |= x << 1; x |= x << 2; x |= x << 4; x |= x << 8; x |= x << 16; return 32 - BitCount(x); }
    public static int LSB(long x) { x |= x << 1; x |= x << 2; x |= x << 4; x |= x << 8; x |= x << 16; x |= x << 32; return 64 - BitCount(x); }
}
static class Ext
{
    public static T[] Fill<T>(this T[] arr, T value)
    {
        for (var i = 0; i < arr.Length; i++) arr[i] = value;
        return arr;
    }
    public static T[] Sort<T>(this T[] arr)
    {
        Array.Sort(arr);
        return arr;
    }
    public static string[] Sort(this string[] arr)
    {
        Array.Sort(arr, StringComparer.OrdinalIgnoreCase);
        return arr;
    }
    public static T[] Reverse<T>(this T[] arr)
    {
        Array.Reverse(arr);
        return arr;
    }

    public static Tuple<TSource, TMax> MaxBy<TSource, TMax>
        (this IEnumerable<TSource> source, Func<TSource, TMax> maxBySelector)
        where TMax : IComparable<TMax>
    {
        TMax max;
        TSource maxByItem;

        var e = source.GetEnumerator();
        e.MoveNext();
        maxByItem = e.Current;
        max = maxBySelector(maxByItem);
        while (e.MoveNext())
        {
            var item = e.Current;
            var next = maxBySelector(item);
            if (max.CompareTo(next) < 0)
            {
                max = next;
                maxByItem = item;
            }
        }
        return Tuple.Create(maxByItem, max);
    }
    public static Tuple<TSource, TMin> MinBy<TSource, TMin>
        (this IEnumerable<TSource> source, Func<TSource, TMin> minBySelector)
        where TMin : IComparable<TMin>
    {
        TMin min;
        TSource minByItem;

        var e = source.GetEnumerator();
        e.MoveNext();
        minByItem = e.Current;
        min = minBySelector(minByItem);
        while (e.MoveNext())
        {
            var item = e.Current;
            var next = minBySelector(item);
            if (min.CompareTo(next) > 0)
            {
                min = next;
                minByItem = item;
            }
        }
        return Tuple.Create(minByItem, min);
    }
    public static IComparer<T> Reverse<T>(this IComparer<T> comparer) => Comparer<T>.Create((x, y) => comparer.Compare(y, x));
    public static Dictionary<TKey, int> GroupCount<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector) => source.GroupBy(keySelector).ToDictionary(g => g.Key, g => g.Count());
    public static Dictionary<TKey, int> GroupCount<TKey>(this IEnumerable<TKey> source) => source.GroupCount(i => i);
}
#pragma warning disable CA1819
[System.Diagnostics.DebuggerNonUserCode]
public class ConsoleReader { private string[] ReadLineSplit() => textReader.ReadLine().Split(Array.Empty<char>(), StringSplitOptions.RemoveEmptyEntries); private string[] line = Array.Empty<string>(); private int linePosition; private TextReader textReader; public ConsoleReader(TextReader tr) { textReader = tr; } public int Int => int.Parse(String); public int Int0 => Int - 1; public long Long => long.Parse(String); public double Double => double.Parse(String); public string String { get { if (linePosition >= line.Length) { linePosition = 0; line = ReadLineSplit(); } return line[linePosition++]; } } public class SplitLine { public SplitLine(ConsoleReader cr) { String = cr.ReadLineSplit(); cr.line = Array.Empty<string>(); } public int[] Int => String.Select(x => int.Parse(x)).ToArray(); public int[] Int0 => String.Select(x => int.Parse(x) - 1).ToArray(); public long[] Long => String.Select(x => long.Parse(x)).ToArray(); public double[] Double => String.Select(x => double.Parse(x)).ToArray(); public string[] String { get; } } public SplitLine Split => new SplitLine(this); public class RepeatReader { ConsoleReader cr; int count; public RepeatReader(ConsoleReader cr, int count) { this.cr = cr; this.count = count; } public T[] Select<T>(Func<ConsoleReader, T> factory) { var arr = new T[count]; for (int i = 0; i < count; i++) arr[i] = factory(cr); return arr; } public string[] String => this.Select(cr => cr.String); public int[] Int => this.Select(cr => cr.Int); public int[] Int0 => this.Select(cr => cr.Int - 1); public long[] Long => this.Select(cr => cr.Long); public double[] Double => this.Select(cr => cr.Double); } public RepeatReader Repeat(int count) => new RepeatReader(this, count); }
public class Program
{
    [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)] private ConsoleReader cr;

    public Program(ConsoleReader consoleReader) { this.cr = consoleReader; }
    static void Main() => Console.WriteLine(new Program(new ConsoleReader(Console.In)).Calc());

    #endregion
    public object Calc()
    {
        var N = cr.Int;
        var robots = cr.Repeat(N).Select(cr => new Robot { x = cr.Int, d = cr.Int });
        Array.Sort(robots, (r1, r2) => r1.x.CompareTo(r2.x));

        var t = new int[N];
        {
            var xs = robots.Select(r => r.x).ToArray();
            for (int i = 0; i < robots.Length; i++)
                t[i] = xs.LowerBound(robots[i].x + robots[i].d);

            var seg = new SegmentTree(t);
            for (int i = t.Length - 1; i >= 0; i--)
            {
                t[i] = seg.Query(i, t[i]);
                seg.Update(i, t[i]);
            }
        }
        Mod[] dp = new Mod[N + 1];
        dp[N] = 1;
        for (int i = N - 1; i >= 0; i--)
        {
            dp[i] = dp[i + 1] + dp[t[i]];
        }
        return dp[0];
    }
}

struct Robot
{
    public int x;
    public int d;
}
static class 二分探索
{
    /// <summary>
    /// 与えられた<paramref name="predicate"/> の結果がtrueであるような最大の数値を二分法で取得します．
    /// </summary>
    /// <param name="l">最小値</param>
    /// <param name="r">最大値</param>
    /// <param name="predicate">判定条件</param>
    /// <returns><paramref name="predicate"/> がtrueを返す最大の o-indexed でのインデックス．</returns>
    /// <remarks> <paramref name="predicate"/> はある範囲以下の全ての数でtrueとなりある範囲以上でfalseとなると仮定します．この関数は O(log N) で実行されます．</remarks>
    static int Bisection(int l, int r, Predicate<int> predicate)
    {
        while (l < r)
        {
            var m = (l + r + 1) >> 1;
            if (predicate(m)) l = m;
            else r = m - 1;
        }
        return l;
    }


    static int BinarySearch<T>(IList<T> a, T v, IComparer<T> cmp, bool isLowerBound)
    {
        var l = 0;
        var r = a.Count - 1;
        while (l <= r)
        {
            var m = (l + r) >> 1;
            var res = cmp.Compare(a[m], v);
            if (res < 0 || (res == 0 && !isLowerBound)) l = m + 1;
            else r = m - 1;
        }
        return l;
    }

    /// <summary>
    /// 与えられた比較関数に従って，<paramref name="a"/> の要素のうち，<paramref name="v"/> 以上の要素であるような最小のインデックスを取得します．
    /// </summary>
    /// <typeparam name="T"><see cref="IList{T}"/> の要素の型を指定します．</typeparam>
    /// <param name="a">対象となるコレクション</param>
    /// <param name="v">対象となる要素</param>
    /// <param name="f"></param>
    /// <returns><paramref name="v"/> 以上の要素であるような最小の o-indexed でのインデックス．</returns>
    /// <remarks> <paramref name="a"/> は比較関数に対して昇順であることを仮定しています．この関数は O(log N) で実行されます．</remarks>
    public static int LowerBound<T>(this IList<T> a, T v, Comparison<T> f) => BinarySearch(a, v, Comparer<T>.Create(f), true);

    /// <summary>
    ///　デフォルトの比較関数に従って，<paramref name="a"/> の要素のうち，<paramref name="v"/> 以上の要素であるような最小のインデックスを取得します．
    /// </summary>
    /// <typeparam name="T"><see cref="IList{T}"/> の要素の型を指定します．</typeparam>
    /// <param name="a">対象となるコレクション</param>
    /// <param name="v">対象となる要素</param>
    /// <param name="f"></param>
    /// <returns><paramref name="v"/> 以上の要素であるような最小の o-indexed でのインデックス．</returns>
    /// <remarks> <paramref name="a"/> は比較関数に対して昇順であることを仮定しています．この関数は O(log N) で実行されます．</remarks>
    public static int LowerBound<T>(this IList<T> a, T v) => BinarySearch(a, v, Comparer<T>.Default, true);

    /// <summary>
    /// 与えられた比較関数に従って，<paramref name="a"/> の要素のうち，<paramref name="v"/> より真に大きい要素が現れる最小のインデックスを取得します．
    /// </summary>
    /// <typeparam name="T"><see cref="IList{T}"/> の要素の型を指定します．</typeparam>
    /// <param name="a">対象となるコレクション</param>
    /// <param name="v">対象となる要素</param>
    /// <param name="f"></param>
    /// <returns><paramref name="v"/> 以上の要素であるような最小の o-indexed でのインデックス．</returns>
    /// <remarks> <paramref name="a"/> は比較関数に対して昇順であることを仮定しています．この関数は O(log N) で実行されます．</remarks>
    public static int UpperBound<T>(this IList<T> a, T v, Comparison<T> cmp) => BinarySearch(a, v, Comparer<T>.Create(cmp), false);

    /// <summary>
    ///　デフォルトの比較関数に従って，<paramref name="a"/> の要素のうち，<paramref name="v"/> より真に大きい要素が現れる最小のインデックスを取得します．
    /// </summary>
    /// <typeparam name="T"><see cref="IList{T}"/> の要素の型を指定します．</typeparam>
    /// <param name="a">対象となるコレクション</param>
    /// <param name="v">対象となる要素</param>
    /// <param name="f"></param>
    /// <returns><paramref name="v"/> 以上の要素であるような最小の o-indexed でのインデックス．</returns>
    /// <remarks> <paramref name="a"/> は比較関数に対して昇順であることを仮定しています．この関数は O(log N) で実行されます．</remarks>
    public static int UpperBound<T>(this IList<T> a, T v) => BinarySearch(a, v, Comparer<T>.Default, false);
}

struct Mod : IEquatable<Mod>
{
    public const long mod = 998244353;
    public readonly long val;
    public Mod(long val, bool isValid) : this(isValid ? val % mod : val) { }
    public Mod(long val) { this.val = val; }
    public override bool Equals(object obj) => (obj is Mod) ? this == ((Mod)obj) : false;
    public bool Equals(Mod obj) => this == obj;
    public override int GetHashCode() => val.GetHashCode();
    public override string ToString() => val.ToString();
    public static implicit operator Mod(long x) => new Mod(x);
    public static Mod operator +(Mod x, Mod y) => (x.val + y.val) % mod;
    public static Mod operator -(Mod x, Mod y) => x.val >= y.val ? (x.val - y.val) % mod : (x.val - y.val) % mod + mod;
    public static Mod operator *(Mod x, Mod y) => (x.val * y.val) % mod;
    public static Mod operator /(Mod x, Mod y) => x * y.Inverse();
    public static bool operator ==(Mod x, Mod y) => x.val == y.val;
    public static bool operator !=(Mod x, Mod y) => x.val != y.val;
    public Mod Inverse()
    {
        long a = val, b = mod, u = 1, v = 0;
        while (b > 0)
        {
            long t = a / b;
            var b2 = a - t * b;
            a = b; b = b2;
            var v2 = u - t * v;
            u = v; v = v2;
        }
        u %= mod;
        if (u < 0) u += mod;
        return u;
    }
    public static Mod Pow(Mod x, int y)
    {
        Mod res = 1;
        for (; y > 0; y >>= 1)
        {
            if ((y & 1) == 1) res *= x;
            x *= x;
        }
        return res;
    }

    public static Factor CreateFactor(int max) => new Factor(max);
    public class Factor
    {
        private readonly Mod[] fac, finv;
        public Factor(int max)
        {
            ++max;
            var inv = new Mod[max];
            fac = new Mod[max]; finv = new Mod[max];
            fac[0] = fac[1] = 1;
            finv[0] = finv[1] = 1;
            inv[1] = 1;
            for (var i = 2; i < max; i++)
            {
                fac[i] = fac[i - 1] * i;
                inv[i] = mod - inv[mod % i].val * (mod / i) % mod;
                finv[i] = finv[i - 1] * inv[i];
            }
        }

        // 二項係数計算
        public Mod Combine(int n, int k)
        {
            if (n < k) return 0;
            if (n < 0 || k < 0) return 0;
            return fac[n] * finv[k] * finv[n - k];
        }

        public Mod Factorial(int n) => fac[n];
        public Mod FactorialInvers(int n) => finv[n];
    }
}
[System.Diagnostics.DebuggerTypeProxy(typeof(SegmentTreeDebugView))]
class SegmentTree
{
    // この辺は場合によって変える
    private int defaultValue = 0;
    private int Operate(int v1, int v2)
    {
        return Math.Max(v1, v2);
    }

    private int rootLength;
    private int[] tree;

    public SegmentTree(int[] initArray) : this(initArray.Length)
    {
        for (var i = 0; i < initArray.Length; i++)
            Update(i, initArray[i]);
    }
    public SegmentTree(int size)
    {
        for (rootLength = 1; rootLength < size; rootLength <<= 1) { }
        tree = NewArray(2 * rootLength - 1, defaultValue);
    }

    public void Update(int index, int value)
    {
        index += rootLength - 1;
        tree[index] = value;
        while (index > 0)
        {
            index = (index - 1) >> 1;
            tree[index] = Operate(tree[index * 2 + 1], tree[index * 2 + 2]);
        }
    }

    public int Query(int fromInclusive, int toExclusive)
    {
        var leftResult = defaultValue;
        var rightResult = defaultValue;
        var l = fromInclusive + rootLength - 1;
        var r = toExclusive + rootLength - 1;

        while (l < r)
        {
            if ((l & 1) == 0)
                leftResult = Operate(leftResult, tree[l]);
            if ((r & 1) == 0)
                rightResult = Operate(tree[r - 1], rightResult);
            l /= 2;
            r = (r - 1) / 2;
        }

        return Operate(leftResult, rightResult);
    }


    [System.Diagnostics.DebuggerDisplay("{" + nameof(value) + "}", Name = "{" + nameof(key) + "}")]
    internal struct KeyValuePairs
    {
        private string key;
        private int value;

        public KeyValuePairs(string key, int value)
        {
            this.key = key;
            this.value = value;
        }
    }
    class SegmentTreeDebugView
    {
        private SegmentTree segmentTree;
        public SegmentTreeDebugView(SegmentTree segmentTree)
        {
            this.segmentTree = segmentTree;
        }

        [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.RootHidden)]
        public KeyValuePairs[] Tree
        {
            get
            {
                var keys = new List<KeyValuePairs>(segmentTree.tree.Length);
                for (var len = segmentTree.rootLength; len > 0; len >>= 1)
                {
                    var unit = segmentTree.rootLength / len;
                    for (var i = 0; i < len; i++)
                    {
                        var index = i + len - 1;
                        if (unit == 1)
                            keys.Add(new KeyValuePairs($"{i}", segmentTree.tree[index]));
                        else
                        {
                            var from = i * unit;
                            keys.Add(new KeyValuePairs($"[{from}-{from + unit})", segmentTree.tree[index]));
                        }
                    }
                }
                return keys.ToArray();
            }
        }
    }
}