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: Accepted)

using System;
using System.Linq;//リストの使用
using System.Collections.Generic;
using System.Text;//テキストの高速出力に必要
class Program
{
	static void Main()
	{
		string[] input = Console.ReadLine().Split(' ');
		long l = long.Parse(input[0]);
		long a = long.Parse(input[1]);
    long b = long.Parse(input[2]);
    long m = long.Parse(input[3]);
    long[,] multipleMatrix = new long[,]
    {
      {1,0,0},
      {0,1,0},
      {0,0,1}
    };
    long[,] firstMatrix = new long[,]
    {
      {0},
      {a%m},
      {1}
    };

    for(int digit = 1; digit <= 18; digit++)
    {
      long[,] subMatrix = new long[,]
      {
        {(MathPow(10,digit))%m,1,0},
        {0,1,b%m},
        {0,0,1}
      };

      long mulCount = SequenceDigitCount(a,b,l,digit);//行列を何乗するか
      subMatrix = MatrixProductRepeat(subMatrix,mulCount,m);
      multipleMatrix = MatrixProduct(subMatrix,multipleMatrix,m);

      //Console.WriteLine(mulCount);
    }

    long[,] answerMatrix = MatrixProduct(multipleMatrix, firstMatrix,m);

		Console.WriteLine(answerMatrix[0,0]%m);
	}

  static long MathPow(long x, long a)//戻り値はx^a
  {
    long num = 2;
    long answer = 1;
    long check = a;
    long memo = x;
    
    while(check > 0)
    {
      if(check % num == num / 2)
      {
        check -= num / 2;
        answer *= memo;
      }
    memo *= memo;
    num *= 2;
    }
    return answer;
  }

  static long SequenceDigitCount(long fir, long tol, long count, long digit)
  {//初項fir, 公差tol, 項数countの等差数列で、桁数digitの整数の個数を返す
    if(fir >= MathPow(10, digit)) return 0;
    if(fir+tol*(count-1) < MathPow(10, digit-1)) return 0;

    long numFirst = Math.Max((MathPow(10,digit-1)-fir+tol-1)/tol, 0);
    long numSecond = 0;

    if(fir+tol*(count-1) >= Math.Pow(10, digit))
    {
      numSecond = (MathPow(10,digit)-1-fir)/tol;
      return (numSecond - numFirst + 1);
    }

    numSecond = count-1;
    return (numSecond - numFirst + 1);
  }

  static long[,] MatrixProductRepeat(long[,] originalMatrix, long mul, long p)
  {//平方行列のmul乗(mod p)を返す
  //ただし、mod=0でそのまま返す
    long matrixLength = originalMatrix.GetLength(0);
    long[,] answerMat = new long[matrixLength,matrixLength];//配列は、=引数とせずnewとしないと引数元も変更される
    for(int i = 0; i < matrixLength; i++)
    {
      for(int j = 0; j < matrixLength; j++)
      {
        answerMat[i,j] = originalMatrix[i,j];
      }
    }
    List<int> multipleList = new List<int>();//0は元の行列、1は積の行列をかける

    long memo = mul;
    while(memo > 1)
    {
      if(memo % 2 == 1)
      {
        multipleList.Add(0);
        memo--;
      }else
      {
        multipleList.Add(1);
        memo /= 2;
      }
    }

    for(int i = multipleList.Count()-1; i >= 0; i--)
    {
      if(multipleList[i] == 0) answerMat = MatrixProduct(answerMat,originalMatrix,p);
      else answerMat = MatrixProduct(answerMat,answerMat,p);
    }

    if(mul == 0)//0乗では単位行列を返す
    {
      for(int i = 0; i < matrixLength; i++)
      {
        for(int j = 0; j < matrixLength; j++)
        {
          if(i == j) answerMat[i,j] = 1;
          else answerMat[i,j] = 0;
        }
      }
    }
    
    return answerMat;
  }

  static long[,] MatrixProduct(long[,] originalMatrixA, long[,] originalMatrixB, long p)
  //行列の積(mod p)を返す
  //ただし、p=0でそのまま返す
  {
    long matrixLengthH = originalMatrixA.GetLength(0);
    long matrixLengthW = originalMatrixB.GetLength(1);
    long matrixLengthSub = originalMatrixA.GetLength(1);
    if(originalMatrixA.GetLength(1) == originalMatrixB.GetLength(0))//これを満たさないと行列計算は不可能
    {
      matrixLengthSub = originalMatrixA.GetLength(1);
    }

    if(p != 0)
    {
      for(int i = 0; i < originalMatrixA.GetLength(0); i++)
      {
        for(int j = 0; j < originalMatrixA.GetLength(1); j++)
        {
          originalMatrixA[i,j] %= p;
        }
      }
      for(int i = 0; i < originalMatrixB.GetLength(0); i++)
      {
        for(int j = 0; j < originalMatrixB.GetLength(1); j++)
        {
          originalMatrixB[i,j] %= p;
        }
      }
    }
    
    long[,] answerM = new long[matrixLengthH, matrixLengthW];
    
    for(long lineM = 0; lineM < matrixLengthH; lineM++)
    {
      for(long rowM = 0; rowM < matrixLengthW; rowM++)
      {
        long ansMemo = 0;
        for(long i = 0; i < matrixLengthSub; i++)
        {
          ansMemo += originalMatrixA[lineM,i] * originalMatrixB[i,rowM];//成分計算
          if(p != 0) ansMemo %= p;
        }
        answerM[lineM,rowM] = ansMemo;
      }
    }
    return answerM;
  }
  
}