Problem:
Score : 600 points

Problem Statement

There is an arithmetic progression with L terms: s_0, s_1, s_2, ... , s_{L-1}.

The initial term is A, and the common difference is B. That is, s_i = A + B \times i holds.

Consider the integer obtained by concatenating the terms written in base ten without leading zeros. For example, the sequence 3, 7, 11, 15, 19 would be concatenated into 37111519. What is the remainder when that integer is divided by M?

Constraints

All values in input are integers.

1 \leq L, A, B < 10^{18}

2 \leq M \leq 10^9

All terms in the arithmetic progression are less than 10^{18}.

Input

Input is given from Standard Input in the following format:

L A B M

Output

Print the remainder when the integer obtained by concatenating the terms is divided by M.

Sample Input 1

5 3 4 10007

Sample Output 1

5563

Our arithmetic progression is 3, 7, 11, 15, 19, so the answer is 37111519 mod 10007, that is, 5563.

Sample Input 2

4 8 1 1000000

Sample Output 2

891011

Sample Input 3

107 10000000000007 1000000000000007 998244353

Sample Output 3

39122908

Solution in C# (Mono 4.6.2.0) (judge verdict: Wrong Answer)

using System;
using System.IO;
using AtCoder;
using CompLib.Mathematics;

namespace AtCoder
{
    class Program
    {

        private long L;
        private long A, B;
        public static long M;
        static void Main(string[] args)
        {
            new Program().Solve();
        }

        public void Solve()
        {
            var sc = new Scanner();
            L = sc.NextLong();
            A = sc.NextLong();
            B = sc.NextLong();
            Program.M = sc.NextInt();
            // そのままだと考え辛いので 1桁のやつ、2桁のやつ...と分けて考える
            // 最後に足す

            // len: 桁

            long m = 1;

            long sZero = A;
            long sLast = A + B * (L - 1);

            ModInt ans = 0;

            for (int len = 1; len <= 18; len++, m *= 10)
            {

                // l <= i < rのときs[i]がlen桁

                if (m * 10 <= sZero)
                {
                    //Console.WriteLine($"{m} {len}");
                    continue;
                }

                if (sLast < m)
                {
                    //Console.WriteLine("!" + $"{m} {len}");
                    continue;
                }

                long l = Math.Max(0, (m - A + B - 1) / B);
                long r = Math.Min(L, (m * 10 - A + B - 1) / B);

                if (r - l <= 0)
                    continue;



                // 行列累乗

                // {0, S[l], B} * {m * 10, 0, 0} = {0*m*10+s[l]*1+B*0, 0*0+S[l]*1+B*1, 0*0+0*S[l]+B*1}
                //                {1     , 1, 0} 
                //                {0     , 1, 1}

                var s = new Matrix(1, 3);
                s[0, 0] = 0;
                s[0, 1] = (ModInt)(A + B * l);
                s[0, 2] = B;

                var t = new Matrix(3, 3);
                t[0, 0] = (ModInt)m * 10;
                t[0, 1] = 0;
                t[0, 2] = 0;
                t[1, 0] = 1;
                t[1, 1] = 1;
                t[1, 2] = 0;
                t[2, 0] = 0;
                t[2, 1] = 1;
                t[2, 2] = 1;
                t = Matrix.Pow(t, r - l);
                s *= t;
                Console.WriteLine($"{len} {r - l}");
                //Console.WriteLine($"{len} {s[0, 0]}");
                //Console.WriteLine($"{len} {l}");
                // s[0,0]がlen桁の時のつなげたやつ

                //　つなげたやつの桁数は len*(r-l)
                // なのでansを10^(len*(r-l)倍する

                ans *= ModInt.Pow(10, len * (r - l));
                ans += s[0, 0];
            }
            Console.WriteLine(ans);
            //Console.WriteLine(111519 % M);
        }
    }
}

namespace CompLib.Mathematics
{
    using N = ModInt;
    #region Matrix
    public class Matrix
    {
        int row, col;
        public N[] mat;
        /// <summary>
        /// <paramref name="r"/> 行 <paramref name="c"/> 列目の要素へのアクセスを提供します。
        /// </summary>
        /// <param name="r">行の番号</param>
        /// <param name="c">列の番号</param>
        public N this[int r, int c]
        {
            get { return mat[r * col + c]; }
            set { mat[r * col + c] = value; }
        }
        public Matrix(int r, int c)
        {
            row = r; col = c;
            mat = new N[row * col];
        }
        public static Matrix operator *(Matrix l, Matrix r)
        {
            System.Diagnostics.Debug.Assert(l.col == r.row);
            var ret = new Matrix(l.row, r.col);
            for (int i = 0; i < l.row; i++)
                for (int k = 0; k < l.col; k++)
                    for (int j = 0; j < r.col; j++)
                        ret.mat[i * r.col + j] = (ret.mat[i * r.col + j] + l.mat[i * l.col + k] * r.mat[k * r.col + j]);
            return ret;
        }
        /// <summary>
        /// <paramref name="m"/>^<paramref name="n"/> を O(<paramref name="m"/>^3 log <paramref name="n"/>) で計算します。
        /// </summary>
        public static Matrix Pow(Matrix m, long n)
        {
            var ret = new Matrix(m.row, m.col);
            for (int i = 0; i < m.row; i++)
                ret.mat[i * m.col + i] = 1;
            for (; n > 0; m *= m, n >>= 1)
                if ((n & 1) == 1)
                    ret = ret * m;
            return ret;

        }
        public N[][] Items
        {
            get
            {
                var a = new N[row][];
                for (int i = 0; i < row; i++)
                {
                    a[i] = new N[col];
                    for (int j = 0; j < col; j++)
                        a[i][j] = mat[i * col + j];
                }
                return a;
            }
        }
    }
    #endregion
}


#region ModInt
/// <summary>
/// [0,<see cref="Mod"/>) までの値を取るような数
/// </summary>
public struct ModInt
{
    /// <summary>
    /// 剰余を取る値．
    /// </summary>
    public static readonly long Mod = Program.M;

    /// <summary>
    /// 実際の数値．
    /// </summary>
    public long num;
    /// <summary>
    /// 値が <paramref name="n"/> であるようなインスタンスを構築します．
    /// </summary>
    /// <param name="n">インスタンスが持つ値</param>
    /// <remarks>パフォーマンスの問題上，コンストラクタ内では剰余を取りません．そのため，<paramref name="n"/> ∈ [0,<see cref="Mod"/>) を満たすような <paramref name="n"/> を渡してください．このコンストラクタは O(1) で実行されます．</remarks>
    public ModInt(long n) { num = n; }
    /// <summary>
    /// このインスタンスの数値を文字列に変換します．
    /// </summary>
    /// <returns>[0,<see cref="Mod"/>) の範囲内の整数を 10 進表記したもの．</returns>
    public override string ToString() { return num.ToString(); }
    public static ModInt operator +(ModInt l, ModInt r) { l.num += r.num; if (l.num >= Mod) l.num -= Mod; return l; }
    public static ModInt operator -(ModInt l, ModInt r) { l.num -= r.num; if (l.num < 0) l.num += Mod; return l; }
    public static ModInt operator *(ModInt l, ModInt r) { return new ModInt(l.num * r.num % Mod); }
    public static implicit operator ModInt(long n) { n %= Mod; if (n < 0) n += Mod; return new ModInt(n); }

    /// <summary>
    /// 与えられた 2 つの数値からべき剰余を計算します．
    /// </summary>
    /// <param name="v">べき乗の底</param>
    /// <param name="k">べき指数</param>
    /// <returns>繰り返し二乗法により O(N log N) で実行されます．</returns>
    public static ModInt Pow(ModInt v, long k) { return Pow(v.num, k); }

    /// <summary>
    /// 与えられた 2 つの数値からべき剰余を計算します．
    /// </summary>
    /// <param name="v">べき乗の底</param>
    /// <param name="k">べき指数</param>
    /// <returns>繰り返し二乗法により O(N log N) で実行されます．</returns>
    public static ModInt Pow(long v, long k)
    {
        long ret = 1;
        for (k %= Mod - 1; k > 0; k >>= 1, v = v * v % Mod)
            if ((k & 1) == 1) ret = ret * v % Mod;
        return new ModInt(ret);
    }
    /// <summary>
    /// 与えられた数の逆元を計算します．
    /// </summary>
    /// <param name="v">逆元を取る対象となる数</param>
    /// <returns>逆元となるような値</returns>
    /// <remarks>法が素数であることを仮定して，フェルマーの小定理に従って逆元を O(log N) で計算します．</remarks>
    public static ModInt Inverse(ModInt v) { return Pow(v, Mod - 2); }
}
#endregion

class Scanner
{
    public Scanner()
    {
        _stream = new StreamReader(Console.OpenStandardInput());
        _pos = 0;
        _line = new string[0];
    }

    const char Separator = ' ';
    private readonly StreamReader _stream;
    private int _pos;
    private string[] _line;

    #region スペース区切りで取得

    public string Next()
    {
        if (_pos >= _line.Length)
        {
            _line = _stream.ReadLine().Split(Separator);
            _pos = 0;
        }

        return _line[_pos++];
    }

    public int NextInt()
    {
        return int.Parse(Next());
    }

    public long NextLong()
    {
        return long.Parse(Next());
    }

    public double NextDouble()
    {
        return double.Parse(Next());
    }

    #endregion

    #region 型変換

    private int[] ToIntArray(string[] array)
    {
        var result = new int[array.Length];
        for (int i = 0; i < array.Length; i++)
        {
            result[i] = int.Parse(array[i]);
        }

        return result;
    }

    private long[] ToLongArray(string[] array)
    {
        var result = new long[array.Length];
        for (int i = 0; i < array.Length; i++)
        {
            result[i] = long.Parse(array[i]);
        }

        return result;
    }

    private double[] ToDoubleArray(string[] array)
    {
        var result = new double[array.Length];
        for (int i = 0; i < array.Length; i++)
        {
            result[i] = double.Parse(array[i]);
        }

        return result;
    }

    #endregion

    #region 配列取得

    public string[] Array()
    {
        if (_pos >= _line.Length)
            _line = _stream.ReadLine().Split(Separator);

        _pos = _line.Length;
        return _line;
    }

    public int[] IntArray()
    {
        return ToIntArray(Array());
    }

    public long[] LongArray()
    {
        return ToLongArray(Array());
    }

    public double[] DoubleArray()
    {
        return ToDoubleArray(Array());
    }

    #endregion
}