Problem:
Score : 400 points

Problem Statement

There are N cells arranged in a row, numbered 1, 2, \ldots, N from left to right.

Tak lives in these cells and is currently on Cell 1. He is trying to reach Cell N by using the procedure described below.

You are given an integer K that is less than or equal to 10, and K non-intersecting segments [L_1, R_1], [L_2, R_2], \ldots, [L_K, R_K].
Let S be the union of these K segments.
Here, the segment [l, r] denotes the set consisting of all integers i that satisfy l \leq i \leq r.

When you are on Cell i, pick an integer d from S and move to Cell i + d. You cannot move out of the cells.

To help Tak, find the number of ways to go to Cell N, modulo 998244353.

Constraints

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

1 \leq K \leq \min(N, 10)

1 \leq L_i \leq R_i \leq N

[L_i, R_i] and [L_j, R_j] do not intersect (i \neq j)

All values in input are integers.

Input

Input is given from Standard Input in the following format:

N K
L_1 R_1
L_2 R_2
:
L_K R_K

Output

Print the number of ways for Tak to go from Cell 1 to Cell N, modulo 998244353.

Sample Input 1

5 2
1 1
3 4

Sample Output 1

4

The set S is the union of the segment [1, 1] and the segment [3, 4], therefore S = \{ 1, 3, 4 \} holds.

There are 4 possible ways to get to Cell 5:

1 \to 2 \to 3 \to 4 \to 5,

1 \to 2 \to 5,

1 \to 4 \to 5 and

1 \to 5.

Sample Input 2

5 2
3 3
5 5

Sample Output 2

0

Because S = \{ 3, 5 \} holds, you cannot reach to Cell 5.
Print 0.

Sample Input 3

5 1
1 2

Sample Output 3

5

Sample Input 4

60 3
5 8
1 3
10 15

Sample Output 4

221823067

Note that you have to print the answer modulo 998244353.

Solution in C# (.NET Core 3.1.201) (judge verdict: Accepted)

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Numerics;
using System.Threading;
//using MultiSet = System.Collections.Generic.Dictionary<T, int>;

public class MainClass
{
    public static void Main(string[] args)
    {
#if DEBUG
        var inputs = new[] { @"
5 2
1 1
3 4
        ", @"
5 2
3 3
5 5
        ", @"
5 1
1 2
        ", @"
60 3
5 8
1 3
10 15
        ", @"

        " };

        var stream = Console.OpenStandardOutput();
        foreach (var str in inputs)
        {
            var text = str.TrimStart();
            if (text.Length == 0) continue;

            //File.ReadAllText(text);
            Console.SetIn(new StringReader(text));
            //using var stream = new FileStream(@$"tester\out_{text}", FileMode.Create);
            Buffered(args, stream);
            Console.WriteLine("----------------------------------");
        }
#else
        Buffered(args, Console.OpenStandardOutput());
#endif
    }

    private static void Buffered(string[] args, Stream stream)
    {
        var sw = new StreamWriter(stream, Console.Out.Encoding);
        sw.AutoFlush = false;
        var beforeOut = Console.Out;
        Console.SetOut(sw);

        Main2(args);

        Console.Out.Flush();
        Console.SetOut(beforeOut);
    }

    //var start = DateTime.Now;
    //if (DateTime.Now - start > TimeSpan.FromMilliseconds(1900)) break;

    private const long INF = long.MaxValue >> 2;

    private static void Main2(string[] args)
    {
        checked
        {
            var /*NK*/ (N, K) = (Int(), Int());

            var R = EnumerablePlus.Repeat(K, () =>
            {
                var /*lr*/ (l, r) = (Int(), Int());
                return new Range(l, r);
            }).ToList();

            var dp = new long[N];
            dp[0] = 1;
            for (int i = 1; i < N; i++)
            {
                foreach (var a in R)
                {
                    if (i - a.l >= 0) a.sum = ModUtil.Add(a.sum, dp[i - a.l]);
                    if (i - a.r - 1 >= 0) a.sum = ModUtil.Sub(a.sum, dp[i - a.r - 1]);
                    dp[i] = ModUtil.Add(dp[i], a.sum);
                }
            }

            Console.WriteLine(dp.Last());
        }
    }

    public class Range
    {
        public int l;
        public int r;
        public long sum = 0;

        public Range(int l, int r)
        {
            this.l = l;
            this.r = r;
        }
    }

    private static void ChMin(ref long a, long b) => a = Math.Min(a, b);
    private static void ChMax(ref long a, long b) => a = Math.Max(a, b);

    #region "入力"

    private static string NextInput = "";

    private static int Int() => int.Parse(Str());
    private static long Long() => long.Parse(Str());
    private static float Float() => float.Parse(Str());
    private static double Double() => double.Parse(Str());
    private static string Str()
    {
        var s = NextInput;
        if (s == "") s = Console.ReadLine().Trim();

        var idx = s.IndexOf(' ');
        if (idx >= 0)
        {
            NextInput = s.Substring(idx + 1).TrimStart();
            return s.Substring(0, idx);
        }
        NextInput = "";
        return s;
    }

    #endregion

}

/// <summary>Union-Find。</summary>
/// <remarks>
/// t[N+1], 1 <= a,b <= N。
/// tの値は以下を表す。
/// ・0:木に属していない
/// ・1以上：親の番号
/// ・-1以下：この値のインデックスはroot。自分含む節の数
/// </remarks>
public static class Uf
{
    public static void Union(int[] t, int a, int b)
    {
        if (t[a] == 0 && t[b] == 0)
        {
            t[a] = -2;
            t[b] = a;
        }
        else if (t[a] != 0 && t[b] != 0)
        {
            var rootA = Find(t, a);
            var rootB = Find(t, b);
            if (rootA != rootB)
            {
                if (t[rootA] > t[rootB]) Util.Swap(ref rootA, ref rootB);
                t[rootA] += t[rootB];
                t[rootB] = rootA;
            }
        }
        else
        {
            if (t[a] == 0) Util.Swap(ref a, ref b);

            var root = Find(t, a);
            t[root]--;
            t[b] = root;
        }
    }

    public static int Find(int[] t, int i)
    {
        var list = new List<int>();
        while (t[i] >= 0)
        {
            if (t[i] == 0) throw new Exception(i.ToString());
            list.Add(i);
            i = t[i];
        }

        foreach (var branch in list) t[branch] = i;

        return i;
    }
}

public static class ComparerPlus
{
    ///<summary>セレクタ関数から昇順のComparerを生成する。</summary>
    public static Comparer<T> By<T, U>(Func<T, U> selector) where U : IComparable
    {
        return Comparer<T>.Create((a, b) => selector(a).CompareTo(selector(b)));
    }

    ///<summary>セレクタ関数から降順のComparerを生成する。</summary>
    public static Comparer<T> ByDecending<T, U>(Func<T, U> selector) where U : IComparable
    {
        return Comparer<T>.Create((a, b) => selector(b).CompareTo(selector(a)));
    }
}

public static class EnumerablePlus
{
    ///<summary>出力文字列に変換。</summary>
    public static string MkString<T>(this IEnumerable<T> list, string sep = null)
    {
        if (sep == null) sep = " ";

        return list.Aggregate(
            new System.Text.StringBuilder(),
            (a, b) => ((a.Length > 0) ? a.Append(sep) : a).Append(b)
            ).ToString();
    }

    ///<summary>添字付きfor。</summary>
    public static void ForEachWithIdx<T>(this IEnumerable<T> list, Action<T, long> a)
    {
        long i = 0;
        foreach (var item in list)
        {
            a(item, i++);
        }
    }

    ///<summary>添字なしfor。</summary>
    public static void ForEach<T>(this IEnumerable<T> list, Action<T> a)
    {
        foreach (var item in list)
        {
            a(item);
        }
    }

    ///<summary>指定回数デリゲートを呼び出す。</summary>
    public static IEnumerable<T> Repeat<T>(long n, Func<T> f)
    {
        for (long i = 0; i < n; i++)
        {
            yield return f();
        }
    }

    ///<summary>条件付きMin。</summary>
    public static T MinBy<T, U>(this IEnumerable<T> xs, Func<T, U> key) where U : IComparable<U>
    {
        return xs.Aggregate((a, b) => key(a).CompareTo(key(b)) <= 0 ? a : b);
    }

    ///<summary>条件付きMax。</summary>
    public static T MaxBy<T, U>(this IEnumerable<T> xs, Func<T, U> key) where U : IComparable<U>
    {
        return xs.Aggregate((a, b) => key(a).CompareTo(key(b)) >= 0 ? a : b);
    }

    ///<summary>条件付きMax。複数の値を返す。</summary>
    public static List<T> MaxByList<T, U>(this IEnumerable<T> xs, Func<T, U> key) where U : IComparable<U>
    {
        return xs.Aggregate(
            new List<T>(),
            (list, b) =>
            {
                if (list.Count == 0) { list.Add(b); return list; }

                var aa = key(list[0]);
                var bb = key(b);

                if (aa.CompareTo(bb) < 0) list.Clear();

                if (aa.CompareTo(bb) <= 0) { list.Add(b); return list; }

                return list;
            });
    }

    ///<summary>複数の値を返す。</summary>
    public static List<T> MaxList<T>(this IEnumerable<T> xs) where T : IComparable<T>
    {
        return xs.MaxByList(o => o);
    }
}

public static class MathPlus
{
    ///<summary>n(>=0)をradix進数に変換する。桁の小さい順に返す。</summary>
    public static List<long> NRadix(long n, long radix)
    {
        if (n < 0) throw new ArgumentOutOfRangeException(nameof(n));
        if (radix < 2) throw new ArgumentOutOfRangeException(nameof(n));

        var res = new List<long>();
        if (n == 0L)
        {
            res.Add(0);
            return res;
        }

        while (n > 0L)
        {
            res.Add(n % radix);
            n /= radix;
        }

        return res;
    }

    ///<summary>べき乗。高速な実装である。</summary>
    public static long Power(long x, long n)
    {
        if (n == 0) return 1;
        if (n % 2 == 0) return Power(x * x, n / 2);
        return x * Power(x, n - 1);
    }

    ///<summary>約数。順番は不定。</summary>
    public static List<long> Divisor(long n)
    {
        var list = Prime(n).
            GroupBy(id => id).
            Select(pair => new { prime = pair.Key, power = pair.Count() }).
            ToList();

        var result = list.Aggregate(new List<long>(), (left, pair) =>
        {
            var list2 = Enumerable.
                Range(0, pair.power + 1).
                Aggregate(new List<long>(), (res, l) =>
                {
                    var next = res.Count > 0 ? res.Last() * pair.prime : 1L;
                    res.Add(next);
                    return res;
                });

            if (left.Count == 0) return list2.ToList();

            return list2.
                SelectMany(l => left.Select(m => l * m)).
                ToList();
        });

        return result;
    }

    ///<summary>順列の数。</summary>
    public static long Permutation(long n, long r)
        => Factorial(n, r);

    ///<summary>組合せの数。</summary>
    public static long Combination(long n, long r)
    {
        if (r == 0) return 1;

        return Factorial(n, r) / Factorial(r);
    }

    ///<summary>階乗。カウントも指定可能。</summary>
    public static long Factorial(long a, long count = long.MaxValue)
    {
        long res = 1;

        for (long i = 0; a > 0 && i < count; i++)
        {
            checked { res *= a; }
            --a;
        }

        return res;
    }

    ///<summary>素因数分解。昇順。</summary>
    public static List<long> Prime(long a)
    {
        var res = new List<long>();

        var a2 = Math.Ceiling(Math.Sqrt(a));
        for (long b = 2; b <= a2; ++b)
        {
            while (a % b == 0)
            {
                res.Add(b);
                a = a / b;
                a2 = Math.Ceiling(Math.Sqrt(a));
            }
        }

        if (a > 1) res.Add(a);
        return res;
    }

    ///<summary>最小公倍数。</summary>
    public static long Lcm(IEnumerable<long> list)
    {
        return list.Aggregate((a, b) => Lcm(a, b));
    }

    ///<summary>最小公倍数。</summary>
    public static long Lcm(long a, long b)
    {
        return checked(a * b) / Gcd(a, b);
    }

    ///<summary>最大公約数。</summary>
    public static long Gcd(IEnumerable<long> list)
    {
        return list.Aggregate((a, b) => Gcd(a, b));
    }

    ///<summary>最大公約数。</summary>
    public static long Gcd(long a, long b)
    {
        if (a < b)
            Gcd(b, a);

        while (b != 0)
        {
            var rem = a % b;
            a = b;
            b = rem;
        }

        return a;
    }

    ///<summary>割り算、かつ整数の切り上げ。</summary>
    public static long Ceiling(long a, long b) => (a + (b - 1)) / b;

    ///<summary>区間内の合計。beginも１から？end含む。</summary>
    public static ulong Sum(ulong begin, ulong end)
    {
        var up = Sum(end);
        var low = Sum(begin - 1);
        return up - low;
    }

    ///<summary>1からの合計。</summary>
    public static ulong Sum(ulong end) => checked((end + 1) * end / 2);
}

public static class Util
{
    ///<summary>リスト内の値を移動する。</summary>
    public static void Move<T>(this IList<T> list, int from, int to)
    {
        var item = list[from];

        for (var idx = from; idx > to; --idx)
        {
            list[idx] = list[idx - 1];
        }

        list[to] = item;
    }

    ///<summary>最後の値か、デフォルト値を取得。</summary>
    public static T LastOrValue<T>(this IList<T> list, T value)
    {
        return list.Count > 0 ? list.Last() : value;
    }

    ///<summary>辞書をMultiSetとして使うときのAdd。</summary>
    public static void AddMulti<T>(this IDictionary<T, int> xs, T key)
    {
        if (xs.ContainsKey(key))
            xs[key]++;
        else
            xs.Add(key, 1);
    }

    ///<summary>値を交換する。</summary>
    public static void Swap(ref int x, ref int y)
    {
        int temp = x;
        x = y;
        y = temp;
    }

    public static void Assert(bool cond)
    {
        if (!cond) throw new Exception();
    }

    /// <summary>
    /// データの範囲に対する二分探索を行う。
    /// </summary>
    /// <param name="min">最小値。</param>
    /// <param name="max">最大値。</param>
    /// <param name="Pred">テスト値と目標値を比較する。テスト値が目標値以下/未満ならtrue。それ以外はfalse。</param>
    /// <param name="CalcCenter"><paramref name="min">と<paramref name="max">の中間値を求める処理。</param>
    /// <typeparam name="T">二分探索するデータ型。</typeparam>
    /// <returns><paramref name="Pred">の戻り値が変わる境界の値を返す。</returns>
    public static (T, T) BinarySearch<T>(T min, T max, Predicate<T> Pred, Func<T, T, T> CalcCenter)
        where T : IComparable<T>, IEquatable<T>
    {
        while (min.CompareTo(max) <= 0)
        {
            var center = CalcCenter(min, max);
            if (center.Equals(min) || center.Equals(max)) break;

            if (Pred(center))
                min = center;
            else
                max = center;
        }

        return (min, max);
    }

    public static (int, int) BinarySearch(int min, int max, Predicate<int> Pred)
    {
        return BinarySearch<int>(min, max, Pred, (a, b) => (a + b) / 2);
    }
}

public static class ModUtil
{
    public const long Mod = 998244353L;

    /// <summary>足し算</summary>
    public static long Add(long a, long b) => (a + b) % Mod;

    /// <summary>引き算</summary>
    public static long Sub(long a, long b) => (a + Mod - b) % Mod;

    /// <summary>掛け算</summary>
    public static long Mul(long a, long b) => ((a % Mod) * (b % Mod)) % Mod;
}