結果

問題 No.3540 Arise
コンテスト
ユーザー nauclhlt
提出日時 2026-03-07 01:15:20
言語 C#
(.NET 10.0.201)
コンパイル:
dotnet_c
実行:
/usr/bin/dotnet_wrap
結果
RE  
実行時間 -
コード長 33,566 bytes
記録
記録タグの例:
初AC ショートコード 純ショートコード 純主流ショートコード 最速実行時間
コンパイル時間 9,238 ms
コンパイル使用メモリ 174,592 KB
実行使用メモリ 215,992 KB
最終ジャッジ日時 2026-05-08 20:54:21
合計ジャッジ時間 24,238 ms
ジャッジサーバーID
(参考情報)
judge2_0 / judge3_1
このコードへのチャレンジ
(要ログイン)
サブタスク 配点 結果
サブタスク1 30 % RE * 19
サブタスク2 70 % AC * 6 RE * 16
合計 3.5 * 0% = 0 点
権限があれば一括ダウンロードができます
コンパイルメッセージ
  復元対象のプロジェクトを決定しています...
  /home/judge/data/code/main.csproj を復元しました (120 ミリ秒)。
  main -> /home/judge/data/code/bin/Release/net10.0/main.dll
  main -> /home/judge/data/code/bin/Release/net10.0/publish/

ソースコード

diff #
raw source code

using System.Diagnostics.CodeAnalysis;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using static Tmpl;
using System.Globalization;
using System.Diagnostics;

StreamWriter writer = new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = false };
Console.SetOut(writer);

Solver.Solve();

Console.Out.Flush();

public static class Solver
{
    private static readonly AtCoderIO cin = new AtCoderIO();

    public static unsafe void Solve()
    {
        int N = cin.Int();
        ModInt<Mod998244353>[] A = cin.ModIntArray<Mod998244353>(N);
        ModInt<Mod998244353>[] invA = new ModInt<Mod998244353>[N];
        for (int i = 0; i < N; i++)
        {
            invA[i] = A[i].Inv();
        }

        Ensure(1 <= N && N <= 1500);

        Array.Sort(A);
        for (int i = 0; i < N; i++)
        {
            if (i < N - 1)
            {
                Ensure(A[i] != A[i + 1]);
            }
        }

        ModInt<Mod998244353> ans = 0L;
        for (int i = 0; i < N; i++)
        {
            ans += (A[i] + 1) / (ModInt<Mod998244353>)2L;
        }

        if ((long)A[0] <= N + 2)
        {
            print(SolveNaive(N, A, invA));
            return;
        }
        
        ModInt<Mod998244353>[] F = new ModInt<Mod998244353>[N + 3];

        for (int x = 1; x <= N + 2; x++)
        {
            ModInt<Mod998244353> left = 1L;
            ModInt<Mod998244353> right = 1L;
            for (int i = 1; i < N; i++)
            {
                right *= (A[i] - x) * invA[i];
            }

            for (int k = 0; k < N; k++)
            {
                F[x] += (A[k] - x) * left * right * invA[k];
                left *= (A[k] - x + 1) * invA[k];
                if (k < N - 1)
                {
                    right /= (ModInt<Mod998244353>)(A[k + 1] - x) * invA[k + 1];
                }
            }
        }
        
        ModInt<Mod998244353>[] G = new ModInt<Mod998244353>[N + 3];
        G[1] = F[1];
        for (int j = 2; j <= N + 2; j++)
        {
            G[j] = G[j - 1] + F[j];
        }

        ModCache<Mod998244353> cache = new(N + 100);

        ModInt<Mod998244353>[] prefix = new ModInt<Mod998244353>[N + 3];
        ModInt<Mod998244353>[] suffix = new ModInt<Mod998244353>[N + 3];

        prefix[0] = 1L;
        suffix[N + 2] = 1L;
        for (int i = 1; i <= N + 2; i++)
        {
            prefix[i] = prefix[i - 1] * (A[0] - i);
        }

        for (int i = N + 1; i >= 0; i--)
        {
            suffix[i] = suffix[i + 1] * (A[0] - i - 1);
        }

        for (int i = 1; i <= N + 2; i++)
        {
            long sign = (i - N + 2) % 2 == 0 ? 1 : -1;
            ans += G[i] * (prefix[i - 1] * suffix[i]) * (cache.InverseFactorial(i - 1) * sign * cache.InverseFactorial(N + 2 - i));
        }

        print(ans);
    }

    private static ModInt<Mod998244353> SolveNaive(int N, ModInt<Mod998244353>[] A, ModInt<Mod998244353>[] invA)
    {
        ModInt<Mod998244353> ans = 0L;
        for (int i = 0; i < N; i++)
        {
            ans += (A[i] + 1) / (ModInt<Mod998244353>)2L;
        }

        for (int m = 1; m <= A[0]; m++)
        {
            ModInt<Mod998244353> left = 1L;
            ModInt<Mod998244353> right = 1L;
            for (int i = 1; i < N; i++)
            {
                right *= (A[i] - m) * invA[i];
            }

            for (int k = 0; k < N; k++)
            {
                ans += (A[k] - m) * left * right * invA[k];
                left *= (A[k] - m + 1) * invA[k];
                if (k < N - 1)
                {
                    right *= A[k + 1] / (A[k + 1] - m);
                }
            }
        }

        return ans;
    }

    private static void Ensure(bool condition)
    {
        if (!condition)
        {
            throw new InvalidOperationException();
        }
    }
}

/// <summary>
/// ModIntの階乗、階乗の逆元、逆元を前計算。
/// </summary>
public sealed class ModCache<T> where T : struct, IMod
{
    private ModInt<T>[] _factorial;
    private ModInt<T>[] _inverseFactorial;
    private ModInt<T>[] _inverse;

    // 階乗(&階乗逆元)と逆元を前計算する.
    // O(max)
    public ModCache(long max)
    {
        _factorial = new ModInt<T>[max + 1];
        _inverseFactorial = new ModInt<T>[max + 1];

        _factorial[0] = 1;
        _inverseFactorial[0] = ModInt<T>.One;

        _inverse = new ModInt<T>[max + 1];
        _inverse[1] = ModInt<T>.CreateFast(1);

        for (long p = 1; p <= max; p++)
        {
            _factorial[p] = _factorial[p - 1] * p;
            if (p > 1)
            {
                _inverse[p] = -(ModInt<T>.Mod / p) * _inverse[ModInt<T>.Mod % p];
            }
            _inverseFactorial[p] = _inverseFactorial[p - 1] * _inverse[p];
        }
    }

    /// <summary>
    /// binom(n, r)を返す。r<0またはr>nのとき0を返す。計算量: O(1)
    /// </summary>
    /// <param name="n"></param>
    /// <param name="r"></param>
    /// <returns></returns>
    public ModInt<T> Combination(long n, long r)
    {
        if (r < 0 || r > n) return 0;
        return _factorial[n] * (_inverseFactorial[n - r] * _inverseFactorial[r]);
    }

    /// <summary>
    /// nPrを返す。r<0またはr>nのとき0を返す。計算量: O(1)
    /// </summary>
    /// <param name="n"></param>
    /// <param name="r"></param>
    /// <returns></returns>
    public ModInt<T> Permutation(long n, long r)
    {
        if (r < 0 || r > n) return 1;
        return _factorial[n] * _inverseFactorial[n - r];
    }

    /// <summary>
    /// n!の値を返す。計算量: O(1)
    /// </summary>
    /// <param name="n"></param>
    /// <returns></returns>
    /// <exception cref="InvalidOperationException"></exception>
    public ModInt<T> Factorial(long n)
    {
        Debug.Assert(0 <= n && n < _factorial.Length);
        return _factorial[n];
    }

    /// <summary>
    /// n!の乗法逆元(n!)^{-1}の値を返す。計算量: O(1)
    /// </summary>
    /// <param name="n"></param>
    /// <returns></returns>
    /// <exception cref="InvalidOperationException"></exception>
    public ModInt<T> InverseFactorial(int n)
    {
        Debug.Assert(0 <= n && n < _inverseFactorial.Length);
        return _inverseFactorial[n];
    }

    /// <summary>
    /// nの乗法逆元n^{-1}を返す。計算量: O(1)
    /// </summary>
    /// <param name="n"></param>
    /// <returns></returns>
    /// <exception cref="InvalidOperationException"></exception>
    public ModInt<T> Inverse(long n)
    {
        Debug.Assert(0 <= n && n < _inverse.Length);
        return _inverse[n];
    }
}

public sealed class AtCoderIO
{
    Queue<string> _readQueue = new Queue<string>();

    private void LoadQueue()
    {
        if (_readQueue.Count > 0) return;
        string line = Console.ReadLine();
        string[] split = line.Split(' ', StringSplitOptions.RemoveEmptyEntries);
        for (int i = 0; i < split.Length; i++) _readQueue.Enqueue(split[i]);
    }

    private void Guard()
    {
        if (_readQueue.Count == 0)
        {
            throw new Exception("NO DATA TO READ");
        }
    }

    public int Int()
    {
        LoadQueue();
        Guard();
        return int.Parse(_readQueue.Dequeue());
    }

    public long Long()
    {
        LoadQueue();
        Guard();
        return long.Parse(_readQueue.Dequeue());
    }

    public string String()
    {
        LoadQueue();
        Guard();
        return _readQueue.Dequeue();
    }

    public short Short()
    {
        LoadQueue();
        Guard();
        return short.Parse(_readQueue.Dequeue());
    }

    public byte Byte()
    {
        LoadQueue();
        Guard();
        return byte.Parse(_readQueue.Dequeue());
    }

    public char Char()
    {
        LoadQueue();
        Guard();
        return char.Parse(_readQueue.Dequeue());
    }

    public double Double()
    {
        LoadQueue();
        Guard();
        return double.Parse(_readQueue.Dequeue());
    }

    public float Float()
    {
        LoadQueue();
        Guard();
        return float.Parse(_readQueue.Dequeue());
    }

    public ModInt<T> ModInt<T>() where T : struct, IMod
    {
        return new ModInt<T>(Long());
    }

    public T Read<T>()
    {
        Type type = typeof(T);
        if (type == typeof(int)) return (T)(object)Int();
        else if (type == typeof(long)) return (T)(object)Long();
        else if (type == typeof(float)) return (T)(object)Float();
        else if (type == typeof(double)) return (T)(object)Double();
        else if (type == typeof(short)) return (T)(object)Short();
        else if (type == typeof(byte)) return (T)(object)Byte();
        else if (type == typeof(char)) return (T)(object)Char();
        else if (type == typeof(string)) return (T)(object)String();
        else return default(T);
    }

    public int[] IntArray(int n)
    {
        if (n == 0) return Array.Empty<int>();

        int[] arr = new int[n];
        for (int i = 0; i < n; i++)
        {
            arr[i] = Int();
        }

        return arr;
    }

    public int[] ZeroIndexedPermutation(int n)
    {
        if (n == 0) return Array.Empty<int>();

        int[] arr = new int[n];
        for (int i = 0; i < n; i++)
        {
            arr[i] = Int() - 1;
        }

        return arr;
    }

    public long[] LongArray(int n)
    {
        if (n == 0) return Array.Empty<long>();

        long[] arr = new long[n];
        for (int i = 0; i < n; i++)
        {
            arr[i] = Long();
        }

        return arr;
    }

    public double[] DoubleArray(int n)
    {
        if (n == 0) return Array.Empty<double>();

        double[] arr = new double[n];
        for (int i = 0; i < n; i++)
        {
            arr[i] = Double();
        }

        return arr;
    }

    public ModInt<T>[] ModIntArray<T>(int n) where T : struct, IMod
    {
        if (n == 0) return Array.Empty<ModInt<T>>();

        ModInt<T>[] arr = new ModInt<T>[n];
        for (int i = 0; i < n; i++)
        {
            arr[i] = (ModInt<T>)Long();
        }

        return arr;
    }

    public T[] ReadArray<T>(int n)
    {
        if (n == 0) return Array.Empty<T>();

        T[] arr = new T[n];
        for (int i = 0; i < n; i++)
        {
            arr[i] = Read<T>();
        }

        return arr;
    }
}

public static class Tmpl
{
    public static long pow(long b, long exp)
    {
        if (exp == 0) return 1;
        if (exp == 1) return b;

        long m = pow(b, exp / 2L);
        m *= m;
        if (exp % 2L == 1) m *= b;

        return m;
    }

    public static long modpow(long b, long exp, long mod)
    {
        if (exp == 0) return 1;
        if (exp == 1) return b % mod;

        long m = modpow(b, exp / 2L, mod);
        m *= m;
		m %= mod;
        if (exp % 2L == 1) m *= b % mod;
        m %= mod;

        return m;
    }

	static long modinv( long a, long mod )
	{
		long b = mod, u = 1, v = 0;
		while ( b > 0 )
		{
			long t = a / b;
			a -= t * b; swap( ref a, ref b );
			u -= t * v; swap( ref u, ref v );
		}
		u %= mod;
		if (u < 0) u += mod;
		return u;
	}

    public static long calcLcm(long a, long b)
    {
        return a * b / calcGcd(a, b);
    }

    public static long calcGcd(long a, long b)
    {
        if (a % b == 0) return b;
        return calcGcd(b, a % b);
    }

    // ax ≡ b (mod m)なる最小のxを求める
    public static bool solveModLinear(long a, long b, long m, out long minSolution)
    {
        long gcd = calcGcd(calcGcd(a, b), m);
        a /= gcd;
        b /= gcd;
        m /= gcd;
        if (calcGcd(a, m) == 1)
        {
            long inv = modinv(a, m);
            minSolution = (b * inv) % m;
            return true;
        }
        minSolution = 0;
        return false;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void YesNo(bool t)
    {
        Console.WriteLine(t ? "Yes" : "No");
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void YESNO(bool t)
    {
        Console.WriteLine(t ? "YES" : "NO");
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void yesno(bool t)
    {
        Console.WriteLine(t ? "yes" : "no");
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool checkBit(int bit, int n)
    {
        return ((1 << n) & bit) != 0;
    }

    public static bool nextSubset(ref int set, int super)
    {
        set = (set - 1) & super;
        return set != 0;
    }

    public static bool nextCombination(int n, int r, int[] array)
    {
        int i = array.Length - 1;
        while (i >= 0 && array[i] == i + n - r)
        {
            i--;
        }

        if (i < 0) return false;

        array[i]++;
        for (int j = i + 1; j < r; j++)
        {
            array[j] = array[j - 1] + 1;
        }

        return true;
    }

    public static bool nextPermutation<T>(T[] array, int start, int length) where T : IComparable<T>
    {
        int end = start + length - 1;

        if (end <= start) return false;

        int last = end;
        while (true)
        {
            int pos = last--;
            
            if (array[last].CompareTo(array[pos]) < 0)
            {
                int i;
                for (i = end + 1; array[last].CompareTo(array[--i]) >= 0; ) { }
                T tmp = array[last]; array[last] = array[i]; array[i] = tmp;
                Array.Reverse(array, pos, end - pos + 1);
                return true;
            }
            if (last == start)
            {
                Array.Reverse(array, start, end - start);
                return false;
            }
        }

        throw new Exception("NextPermutation: Fatal error");
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool chmax<T> (ref T target, T value) where T : struct, IComparisonOperators<T, T, bool>
    {
        if (value > target)
        {
            target = value;
            return true;
        }
        return false;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static bool chmin<T>(ref T target, T value) where T : struct, IComparisonOperators<T, T, bool>
    {
        if (value < target)
        {
            target = value;
            return true;
        }
        return false;
    }

    public static ReadOnlySpan<T> spanOf<T>(List<T> list)
    {
        return CollectionsMarshal.AsSpan(list);
    }

    public static int lowerBound<T>(T[] array, int start, int end, T value) where T : struct, IComparisonOperators<T, T, bool>
    { 
        int low = start;
        int high = end;
        int mid;
        while (low < high)
        {
            mid = ((high - low) >> 1) + low;
            if (array[mid] < value)
                low = mid + 1;
            else
                high = mid;
        }

        if (low == array.Length - 1 && array[low] < value)
        {
            return array.Length;
        }
        return low;
    }

    public static int lowerBound<T>(List<T> array, int start, int end, T value) where T : struct, IComparisonOperators<T, T, bool>
    { 
        int low = start;
        int high = end;
        int mid;
        while (low < high)
        {
            mid = ((high - low) >> 1) + low;
            if (array[mid] < value)
                low = mid + 1;
            else
                high = mid;
        }

        if (low == array.Count - 1 && array[low] < value)
        {
            return array.Count;
        }
        return low;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void swap<T>(ref T a, ref T b) where T : struct
    {
        T temp = a;
        a = b;
        b = temp;
    }

    public static int[] compressCoords<T>(T[] a) where T : struct, IComparisonOperators<T, T, bool>
    {
        T[] b = a.OrderBy(x => x).Distinct().ToArray();
        int[] result = new int[a.Length];

        for (int i = 0; i < a.Length; i++)
        {
            result[i] = lowerBound(b, 0, b.Length - 1, a[i]);
        }

        return result;
    }

    public static List<RunLengthElement<T>> compressRunLength<T>(T[] array) where T : struct, IEquatable<T>
    {
        List<RunLengthElement<T>> list = new List<RunLengthElement<T>>();

        int count = 1;
        int start = 0;
        T prev = array[0];

        for (int i = 1; i < array.Length; i++)
        {
            if (prev.Equals(array[i]))
            {
                count++;
            }
            else
            {
                list.Add(new RunLengthElement<T>(start, count, prev));
                start = i;
                count = 1;
            }

            prev = array[i];
        }

        list.Add(new RunLengthElement<T>(start, count, prev));

        return list;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void print<T>(IEnumerable<T> array, char delimiter = ' ')
    {
        Console.WriteLine(string.Join(delimiter, array));
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void fprint<T>(IEnumerable<T> array, char delimiter = ' ')
    {
        print(array);
        Console.Out.Flush();
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void print(string msg)
    {
        Console.WriteLine(msg);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void fprint(string msg)
    {
        Console.WriteLine(msg);
        Console.Out.Flush();
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void print(int val)
    {
        print(val.ToString());
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void print(long val)
    {
        print(val.ToString());
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void print<T>(ModInt<T> val) where T : struct, IMod
    {
        print(val.ToString());
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void fprint(int val)
    {
        print(val.ToString());
        Console.Out.Flush();
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void fprint(long val)
    {
        print(val.ToString());
        Console.Out.Flush();
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void fprint<T>(ModInt<T> val) where T : struct, IMod
    {
        fprint(val.ToString());
        Console.Out.Flush();
    }

    public static void print<T>(T[,] grid)
    {
        for (int y = 0; y < grid.GetLength(0); y++)
        {
            for (int x = 0; x < grid.GetLength(1); x++)
            {
                Console.Write(grid[y, x] + "\t");
            }
            Console.WriteLine();
        }
    }

    public static void print01(bool t)
    {
        Console.WriteLine(t ? "1" : "0");
    }

    public static TSrc lowerBoundKth<TSrc, TCnt>(TSrc min, TSrc max, Func<TSrc, TCnt> f, TCnt bound)
        where TSrc: struct, INumber<TSrc> where TCnt: struct, INumber<TCnt>
    {
        TSrc left = min;
        TSrc right = max;
        TSrc two = TSrc.CreateChecked(2);
        TSrc one = TSrc.CreateChecked(1);
        while (right > left)
        {
            TSrc mid = left + (right - left) / two;
            TCnt c = f(mid);
            if (c < bound)
            {
                left = mid + one;
            }
            else
            {
                right = mid;
            }
        }

        return left;
    }

    public static bool isSquareNumber(long n)
    {
        long q = (long)Math.Floor(Math.Sqrt(n));
        return q * q == n;
    }

    public static T[][] makeDPTable2D<T>(int length1, int length2, T value)
    {
        bool fill = !default(T).Equals(value);
        T[][] table = new T[length1][];
        for (int i = 0; i < length1; i++)
        {
            table[i] = new T[length2];
            if (fill)
            {
                Array.Fill(table[i], value);
            }
        }

        return table;
    }

    public static T[][][] makeDPTable3D<T>(int length1, int length2, int length3, T value)
    {
        bool fill = !default(T).Equals(value);
        T[][][] table = new T[length1][][];
        for (int i = 0; i < length1; i++)
        {
            table[i] = new T[length2][];
            for (int j = 0; j < length2; j++)
            {
                table[i][j] = new T[length3];
                if (fill)
                {
                    Array.Fill(table[i][j], value);
                }
            }
        }

        return table;
    }
}

public readonly struct RunLengthElement<T> where T : struct
{
    public readonly int Count;
    public readonly int StartIndex;
    public readonly T Value;

    public RunLengthElement(int startIndex, int count, T value)
    {
        this.StartIndex = startIndex;
        this.Count = count;
        this.Value = value;
    }
}

/// <summary>
/// 自動でmodをとる整数。必要ないならあんまり使いすぎないように。
/// パフォーマンスはあまり良くないかもしれない。
/// </summary>
/// <typeparam name="T"></typeparam>
public readonly struct ModInt<T> : INumber<ModInt<T>>
                                    where T : struct, IMod
{
    public static long Mod => _mod.Mod;

    private static readonly T _mod = default;

    public readonly long Value;

    /// <summary>
    /// 1を返す。計算量: O(1)
    /// </summary>
    public static ModInt<T> One { get; } = CreateFast(1L);

    /// <summary>
    /// 0を返す。計算量: O(1)
    /// </summary>
    public static ModInt<T> Zero { get; } = CreateFast(0L);

    public static int Radix => 10;
    public static ModInt<T> MinValue => CreateFast(0L);
    public static ModInt<T> MaxValue => CreateFast(_mod.Mod - 1);

    /// <summary>
    /// 加法単位元つまり0を返す。計算量: O(1)
    /// </summary>
    public static ModInt<T> AdditiveIdentity { get; } = CreateFast(0L);
    public static ModInt<T> MultiplicativeIdentity { get; } = CreateFast(1L);

    public ModInt(long value)
    {
        Value = SafeMod(value);
    }

    private ModInt(long value, bool dummy)
    {
        Value = value;
    }

    /// <summary>
    /// modintを構築する。0 <= value < MODのとき限定。速いはず。計算量: O(1)
    /// </summary>
    /// <param name="value"></param>
    /// <returns></returns>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static ModInt<T> CreateFast(long value)
    {
        return new ModInt<T>(value, false);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    private static long SafeMod(long a)
    {
        return a % _mod.Mod + ((a >> 63) & _mod.Mod);
    }

    /// <summary>
    /// exp乗を計算する。計算量: O(log(exp))
    /// </summary>
    /// <param name="exp"></param>
    /// <returns></returns>
    public readonly ModInt<T> Power(long exp)
    {
        if (exp < 0)
        {
            return Power(-exp).Inv();
        }
        else
        {
            long res = 1L;
            long b = Value;
            while (exp > 0)
            {
                if ((exp & 1) == 1) res = res * b % _mod.Mod;
                b = b * b % _mod.Mod;
                exp >>= 1;
            }

            return CreateFast(res);
        }
    }

    /// <summary>
    /// 乗法逆元を返す。0は渡さない。計算量: O(logN)
    /// </summary>
    /// <returns></returns>
    public readonly ModInt<T> Inv()
    {
        long x = 1, y = 0;
        long x1 = 0, y1 = 1;
        long b = _mod.Mod;
        long a = Value;

        while (b != 0)
        {
            long q = a / b;
            long t = a % b;
            a = b;
            b = t;

            long tx = x - q * x1;
            long ty = y - q * y1;
            x = x1;
            y = y1;
            x1 = tx;
            y1 = ty;
        }

        return new(x);
    }

    [MethodImpl(256)]
    public static ModInt<T> operator +(ModInt<T> left, ModInt<T> right) => CreateFast((left.Value + right.Value) % _mod.Mod);

    [MethodImpl(256)]
    public static ModInt<T> operator +(ModInt<T> self) => self;

    [MethodImpl(256)]
    public static ModInt<T> operator -(ModInt<T> left, ModInt<T> right) => CreateFast((left.Value - right.Value + _mod.Mod) % _mod.Mod);

    [MethodImpl(256)]
    public static ModInt<T> operator -(ModInt<T> self) => CreateFast(_mod.Mod - self.Value);

    [MethodImpl(256)]
    public static ModInt<T> operator *(ModInt<T> left, ModInt<T> right) => CreateFast(left.Value * right.Value % _mod.Mod);

    [MethodImpl(256)]
    public static ModInt<T> operator /(ModInt<T> left, ModInt<T> right)
    {
        if (right.Value == 0L)
        {
            throw new DivideByZeroException();
        }

        ModInt<T> inv = right.Inv();
        return CreateFast(left.Value * inv.Value % _mod.Mod);
    }

    public static ModInt<T> operator %(ModInt<T> left, ModInt<T> right)
    {
        throw new NotImplementedException();
    }

    public static bool operator <(ModInt<T> left, ModInt<T> right)
    {
        throw new NotImplementedException();
    }

    public static bool operator >(ModInt<T> left, ModInt<T> right)
    {
        throw new NotImplementedException();
    }

    public static bool operator <=(ModInt<T> left, ModInt<T> right)
    {
        throw new NotImplementedException();
    }

    public static bool operator >=(ModInt<T> left, ModInt<T> right)
    {
        throw new NotImplementedException();
    }

    [MethodImpl(256)]
    public static bool operator ==(ModInt<T> left, ModInt<T> right) => left.Value == right.Value;

    [MethodImpl(256)]
    public static bool operator !=(ModInt<T> left, ModInt<T> right) => !(left == right);

    [MethodImpl(256)]
    public static ModInt<T> operator ++(ModInt<T> self) => CreateFast((self.Value + 1) % _mod.Mod);

    [MethodImpl(256)]
    public static ModInt<T> operator --(ModInt<T> self) => CreateFast((self.Value - 1 + _mod.Mod) % _mod.Mod);

    [MethodImpl(256)]
    public bool Equals(ModInt<T> other) => Value == other.Value;

    [MethodImpl(256)]
    public override bool Equals(object other)
    {
        if (other is ModInt<T> m)
        {
            return this == m;
        }
        else return false;
    }

    [MethodImpl(256)]
    public override int GetHashCode() => Value.GetHashCode();

    [MethodImpl(256)]
    public static implicit operator ModInt<T>(long v) => new ModInt<T>(v);

    [MethodImpl(256)]
    public static implicit operator ModInt<T>(int v) => new ModInt<T>(v);

    [MethodImpl(256)]
    public static implicit operator long(in ModInt<T> m) => m.Value;

    [MethodImpl(256)]
    public static implicit operator int(in ModInt<T> m) => (int)m.Value;

    public override string ToString() => Value.ToString();

    public string ToString(string format, IFormatProvider provider) => Value.ToString(format, provider);

    #region INumberBase<TSelf> Implementation

    public static ModInt<T> Abs(ModInt<T> value) => value;
    public static bool IsCanonical(ModInt<T> value) => true;
    public static bool IsComplexNumber(ModInt<T> value) => false;
    public static bool IsFinite(ModInt<T> value) => true;
    public static bool IsImaginaryNumber(ModInt<T> value) => false;
    public static bool IsInfinity(ModInt<T> value) => false;
    public static bool IsInteger(ModInt<T> value) => true;
    public static bool IsNaN(ModInt<T> value) => false;
    public static bool IsNegative(ModInt<T> value) => false;
    public static bool IsPositive(ModInt<T> value) => value.Value != 0L;
    public static bool IsRealNumber(ModInt<T> value) => true;
    public static bool IsZero(ModInt<T> value) => value.Value == 0L;
    public static bool IsEvenInteger(ModInt<T> value) => (value.Value & 1) == 0;
    public static bool IsOddInteger(ModInt<T> value) => (value.Value & 1) == 1;
    public static bool IsPositiveInfinity(ModInt<T> value) => false;
    public static bool IsNegativeInfinity(ModInt<T> value) => false;
    public static bool IsNormal(ModInt<T> value) => false;
    public static bool IsSubnormal(ModInt<T> value) => false;


    public static ModInt<T> MaxMagnitude(ModInt<T> x, ModInt<T> y)
    {
        if (x.Value > y.Value) return x;
        else return y;
    }
    public static ModInt<T> MaxMagnitudeNumber(ModInt<T> x, ModInt<T> y) => MaxMagnitude(x, y);
    public static ModInt<T> MinMagnitude(ModInt<T> x, ModInt<T> y)
    {
        if (x.Value < y.Value) return x;
        else return y;
    }
    public static ModInt<T> MinMagnitudeNumber(ModInt<T> x, ModInt<T> y) => MinMagnitude(x, y);

    public static ModInt<T> CreateChecked<TOther>(TOther value) where TOther : INumberBase<TOther>
        => new(long.CreateChecked(value));
    public static ModInt<T> CreateSaturating<TOther>(TOther value) where TOther : INumberBase<TOther>
        => new(long.CreateSaturating(value));
    public static ModInt<T> CreateTruncating<TOther>(TOther value) where TOther : INumberBase<TOther>
        => new(long.CreateTruncating(value));

    public static ModInt<T> Parse(string s, NumberStyles style, IFormatProvider provider) => Parse(s.AsSpan(), style, provider);

    public static ModInt<T> Parse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider provider) => new(long.Parse(s, style, provider));

#if NET8_0_OR_GREATER
    public static ModInt<T> Parse(ReadOnlySpan<byte> s, NumberStyles style, IFormatProvider provider) => new(long.Parse(s, style, provider));
#endif

    public static ModInt<T> Parse(string s, IFormatProvider provider) => new(long.Parse(s, provider));

    public static ModInt<T> Parse(ReadOnlySpan<char> s, IFormatProvider provider) => new(long.Parse(s, provider));

#if NET8_0_OR_GREATER
    public bool TryFormat(Span<byte> dest, out int bytesWritten, ReadOnlySpan<char> format, IFormatProvider provider)
    {
        return Value.TryFormat(dest, out bytesWritten, format, provider);
    }
#endif

    public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format, IFormatProvider provider)
    {
        return Value.TryFormat(destination, out charsWritten, format, provider);
    }

    public static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider provider, out ModInt<T> result)
    {
        if (long.TryParse(s, style, provider, out long inner))
        {
            result = new(inner);
            return true;
        }
        else
        {
            result = Zero;
            return false;
        }

    }

    public static bool TryParse(string s, NumberStyles style, IFormatProvider provider, out ModInt<T> result)
    {
        if (long.TryParse(s, style, provider, out long inner))
        {
            result = new(inner);
            return true;
        }
        else
        {
            result = Zero;
            return false;
        }
    }



    public static bool TryParse(ReadOnlySpan<char> s, IFormatProvider provider, out ModInt<T> result)
    {
        if (long.TryParse(s, provider, out long inner))
        {
            result = new(inner);
            return true;
        }
        else
        {
            result = Zero;
            return false;
        }
    }

    public static bool TryParse(string s, IFormatProvider provider, out ModInt<T> result)
    {
        if (long.TryParse(s, provider, out long inner))
        {
            result = new(inner);
            return true;
        }
        else
        {
            result = Zero;
            return false;
        }
    }

    public static bool TryConvertFromChecked<TOther>(TOther value, out ModInt<T> result) where TOther : INumberBase<TOther>
    {
        throw new NotImplementedException();
    }

    public static bool TryConvertFromSaturating<TOther>(TOther value, out ModInt<T> result) where TOther : INumberBase<TOther>
    {
        throw new NotImplementedException();
    }

    public static bool TryConvertFromTruncating<TOther>(TOther value, [MaybeNullWhen(false)] out ModInt<T> result) where TOther : INumberBase<TOther>
    {
        throw new NotImplementedException();
    }

    public static bool TryConvertToChecked<TOther>(ModInt<T> value, [MaybeNullWhen(false)] out TOther result) where TOther : INumberBase<TOther>
    {
        throw new NotImplementedException();
    }

    public static bool TryConvertToSaturating<TOther>(ModInt<T> value, [MaybeNullWhen(false)] out TOther result) where TOther : INumberBase<TOther>
    {
        throw new NotImplementedException();
    }

    public static bool TryConvertToTruncating<TOther>(ModInt<T> value, [MaybeNullWhen(false)] out TOther result) where TOther : INumberBase<TOther>
    {
        throw new NotImplementedException();
    }


    #endregion

    #region INumber<TSelf> Implementation

    public int CompareTo(ModInt<T> other) => Value.CompareTo(other.Value);
    public int CompareTo(object other) => Value.CompareTo(other);
    

    #endregion
}

/// <summary>
/// 法を指定するやつ。
/// </summary>
public interface IMod
{
    public long Mod { get; }
}

public readonly struct Mod998244353 : IMod { public long Mod => 998244353L; }
public readonly struct Mod1000000007 : IMod { public long Mod => 1000000007L; }
public readonly struct Mod897581057 : IMod { public long Mod => 897581057; }
public readonly struct Mod880803841 : IMod { public long Mod => 880803841; }
0