using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Numerics;

namespace Problem51Nod
{
class Program
{
public static void Main(String[] args)
{
//BigInteger m = BigInteger.Pow(5, 143) - 1;
//BigInteger n = BigInteger.Pow(2, 331) + 1;
//int k = 100;

//Stopwatch timer = new Stopwatch();
//timer.Start();
Console.WriteLine(Cmn(m, n, k));
//timer.Stop();
//Console.WriteLine(timer.Elapsed);
}

static BigInteger[][] PowerTable = new BigInteger[6][];
static List<BigInteger[][]>[] PreTable = new List<BigInteger[][]>[6];
static BigInteger[,] CMatrix;

static void InitCMatrix(int l)
{
CMatrix = new BigInteger[l + 1, l + 1];
for (int i = 0; i <= l; i++)
CMatrix[i, 0] = CMatrix[i, i] = 1;

for (int i = 2; i <= l; i++)
for (int j = 1; j * 2 <= i; j++)
CMatrix[i, j] = CMatrix[i, i - j] = CMatrix[i - 1, j - 1] + CMatrix[i - 1, j];
}

static void InitPowerTab(int l, int p)
{
PowerTable[p] = new BigInteger[l + 1];
PowerTable[p][0] = 1;

for (int j = 1; j < PowerTable[p].Length; j++)
PowerTable[p][j] = PowerTable[p][j - 1] * p;
}

static void InitData(int l, BigInteger max, int p)
{
PreTable[p] = new List<BigInteger[][]>();
BigInteger[] pre = MulTo(l, p);
BigInteger[,] cMatrix = new BigInteger[l + 1, l + 1];

for (int i = 0; i <= l; i++)
for (int j = 0; j <= i; j++)
cMatrix[i, j] = CMatrix[i, j];

int power = 1;
for (BigInteger i = p; i <= max; i *= p)
{
BigInteger[][] cTable = new BigInteger[p - 1][];
cTable[0] = pre;
for (int j = 1; j < p; j++)
{
BigInteger[] next = ReplaceWith(cTable[0], i * j % PowerTable[p][l], l, p, cMatrix);

if (j < p - 1)
cTable[j] = MulMod(cTable[j - 1], next, l, p);
else
pre = MulMod(cTable[j - 1], next, l, p);
}

power++;
}
}

static BigInteger Cmn(BigInteger m, BigInteger n, int l)
{
InitCMatrix(l);
InitPowerTab(l, 5);
InitPowerTab(l, 2);
BigInteger up5 = CalP(m, n, 5, l);
BigInteger up2 = CalP(m, n, 2, l);
BigInteger mod = ((up5 - up2) + PowerTable[5][l]) % PowerTable[5][l];
mod = IMod(PowerTable[2][l], mod, PowerTable[5][l]) * PowerTable[2][l] + up2;
return mod;
}

static BigInteger CalP(BigInteger m, BigInteger n, int p, int l)
{
BigInteger count = Count(m, p) - Count(n, p) - Count(m - n, p);
if (count > l) return 0;
InitData(l, m, p);
BigInteger up = Cal(m, p, l) * PowerTable[p][(int)count];
BigInteger down = Cal(n, p, l);
down *= Cal(m - n, p, l);
down %= PowerTable[p][l];
up = IMod(down, up, PowerTable[p][l]);
return up;
}

static BigInteger Count(BigInteger v, BigInteger mod)
{
BigInteger count = 0;

while (v > 0)
{
v /= mod;
count += v;
}

return count;
}

static List<BigInteger> Values = new List<BigInteger>();
static List<int> Mods = new List<int>();

static BigInteger Cal(BigInteger nums, int p, int l)
{
BigInteger numsBak = nums;
Values.Clear();
Mods.Clear();

while (numsBak > 0)
{
numsBak /= p;
}

BigInteger result = 1;

for (int i = 0; i < Values.Count; i++)
{
result *= CalSingle(i, p, l);
result %= PowerTable[p][l];
}

return result;
}

static BigInteger CalSingle(int cIndex, int p, int l)
{
int len = Mods[cIndex];
BigInteger sum = 0, last = (Values[cIndex] - len) % PowerTable[p][l];
BigInteger[] pre = new BigInteger[] { 1 };
BigInteger result = 1;
cIndex++;

for (int i = Mods.Count - 1; i >= cIndex; i--)
{
int index = Mods[i];

if (index > 0)
{
BigInteger modValue = 1, current = 0;
foreach (var item in PreTable[p][i - cIndex][index - 1])
{
if (item != 0)
current = (current + modValue * item) % PowerTable[p][l];

if (sum == 0 || modValue == 0)
break;

modValue = modValue * sum % PowerTable[p][l];
}

if (i - cIndex + 1 <= l)
sum = (sum + index * PowerTable[p][i - cIndex + 1]) % PowerTable[p][l];

result = (result * current) % PowerTable[p][l];
}
}

for (int i = 1; i <= len; i++)
{
if (i % p == 0)
continue;

result *= last + i;
result %= PowerTable[p][l];
}

return result;
}

static BigInteger[] MulTo(int l, int p)
{
BigInteger[] result = new BigInteger[] { 1 };

for (BigInteger i = 1; i < p; i++)
{
if (i % p == 0)
continue;

BigInteger[] b = new BigInteger[] { i, 1 };
result = MulMod(result, b, l, p);
}

return result;
}

static BigInteger[] MulMod(BigInteger[] a, BigInteger[] b, int l, int p)
{
int len = Math.Min(l + 1, a.Length + b.Length - 1);
BigInteger[] result = new BigInteger[len];

for (int i = 0; i < Math.Min(a.Length, l + 1); i++)
{
if (a[i] == 0)
continue;

int upper = Math.Min(b.Length, l - i + 1);
for (int j = 0; j < upper; j++)
result[i + j] += a[i] * b[j];
}

int last = 0;

for (int i = 0; i < result.Length; i++)
{
result[i] %= PowerTable[p][l - i];
if (result[i] > 0)
last = i + 1;
}

if (last < result.Length)
Array.Resize(ref result, last);

return result;
}

//2次方算法，考虑大数是3次方 除以大进制的常数
static BigInteger[] ReplaceWith(BigInteger[] source, BigInteger into, int l, int p, BigInteger[,] cMatrix)
{
int len = source.Length;
len = Math.Min(len, l + 1);
BigInteger[] result = new BigInteger[len];
BigInteger[] power = new BigInteger[len];
power[0] = 1;

for (int i = 1; i < len; i++)
power[i] = power[i - 1] * into % PowerTable[p][l];

for (int i = 0; i < len; i++)
{
if (source[i] == 0)
continue;

for (int j = 0; j <= i; j++)
{
if (power[i] == 0) continue;
var tmp = power[i - j] * source[i] % PowerTable[p][l - j];
if (tmp == 0) continue;
result[j] += tmp * cMatrix[i, j];
}
}

for (int i = 0; i < result.Length; i++)
result[i] %= PowerTable[p][l - i];

return result;
}

public static BigInteger EuclidExtend(BigInteger X, BigInteger Y, out BigInteger A, out BigInteger B)
{
if (Y == 0) { A = 1; B = 0; return X; }
BigInteger quotient = X / Y;
BigInteger gcd = EuclidExtend(Y, X - Y * quotient, out A, out B);
BigInteger Temp = A; A = B; B = Temp - quotient * A;
return gcd;
}

public static bool Linear(BigInteger X, BigInteger Y, BigInteger N, out BigInteger xResult, out BigInteger yResult)
{
BigInteger gcd = EuclidExtend(X, Y, out xResult, out yResult);
if (N % gcd != 0) { return false; }
xResult = xResult * N / gcd % Y;
xResult = xResult >= 0 ? xResult : xResult + Y;
yResult = yResult * N / gcd % X;
yResult = yResult <= 0 ? yResult : yResult - X;
return true;
}

public static BigInteger IMod(BigInteger A, BigInteger B, BigInteger P)
{
BigInteger x, y;
Linear(A, P, B, out x, out y);
return x;
}
}
}
View Code