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;

namespace AtCoder
{
    class Program
    {

        private long L;
        private long A, B;
        public 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();
            M = sc.NextInt();
            // そのままだと考え辛いので 1桁のやつ、2桁のやつ...と分けて考える
            // 最後に足す

            // len: 桁

            long m = 1;

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

            long l = -1, r = 0;

            bool f = false;

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

                l = r;

                if (sLast < m)
                {
                    break;
                }

                r = Math.Min(L, (m * 10 - A + B - 2) / B);


                // 行列累乗

                // {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 long[1, 3];
                s[0, 0] = 0;
                s[0, 1] = (A + B * l) % M;
                s[0, 2] = B;

                var t = new long[3, 3];
                t[0, 0] = (m * 10) % M;
                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;

                long b = r - l;
                while (b > 0)
                {
                    if (b % 2 == 1)
                    {
                        var nexts = new long[1, 3];
                        for (int i = 0; i < 1; i++)
                        {
                            for (int j = 0; j < 3; j++)
                            {
                                for (int k = 0; k < 3; k++)
                                {
                                    nexts[i, j] += (s[i, k] * t[k, j]) % M;
                                    nexts[i, j] %= M;
                                }
                            }
                        }

                        s = nexts;
                    }

                    var next = new long[3, 3];
                    for (int i = 0; i < 3; i++)
                    {
                        for (int j = 0; j < 3; j++)
                        {
                            for (int k = 0; k < 3; k++)
                            {
                                next[i, j] += (t[i, k] * t[k, j]) % M;
                                next[i, j] %= M;
                            }
                        }
                    }

                    t = next;

                    b /= 2;
                }
                Console.WriteLine($"{len} {l} {r}");
                //Console.WriteLine($"{len} {s[0, 0]}");
                //Console.WriteLine($"{len} {l}");
                // s[0,0]がlen桁の時のつなげたやつ

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

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


class MyMath
{
    public static long Pow(long a, long b, long mod)
    {
        long result = 1;
        while (b > 0)
        {
            if (b % 2 == 1)
            {
                result *= a;
                result %= mod;
            }

            a *= a;
            a %= mod;
            b /= 2;
        }

        return result;
    }
}

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
}