結果
問題 | No.1694 ZerOne |
ユーザー | terry_u16 |
提出日時 | 2021-10-01 22:15:32 |
言語 | C# (.NET 8.0.203) |
結果 |
TLE
|
実行時間 | - |
コード長 | 38,567 bytes |
コンパイル時間 | 10,390 ms |
コンパイル使用メモリ | 173,268 KB |
実行使用メモリ | 116,816 KB |
最終ジャッジ日時 | 2024-07-19 11:36:23 |
合計ジャッジ時間 | 13,863 ms |
ジャッジサーバーID (参考情報) |
judge1 / judge3 |
(要ログイン)
テストケース
テストケース表示入力 | 結果 | 実行時間 実行使用メモリ |
---|---|---|
testcase_00 | AC | 57 ms
33,536 KB |
testcase_01 | AC | 55 ms
35,712 KB |
testcase_02 | AC | 54 ms
30,336 KB |
testcase_03 | TLE | - |
testcase_04 | -- | - |
testcase_05 | -- | - |
testcase_06 | -- | - |
testcase_07 | -- | - |
testcase_08 | -- | - |
testcase_09 | -- | - |
testcase_10 | -- | - |
testcase_11 | -- | - |
testcase_12 | -- | - |
testcase_13 | -- | - |
testcase_14 | -- | - |
testcase_15 | -- | - |
testcase_16 | -- | - |
testcase_17 | -- | - |
testcase_18 | -- | - |
testcase_19 | -- | - |
testcase_20 | -- | - |
testcase_21 | -- | - |
testcase_22 | -- | - |
testcase_23 | -- | - |
testcase_24 | -- | - |
testcase_25 | -- | - |
testcase_26 | -- | - |
testcase_27 | -- | - |
testcase_28 | -- | - |
testcase_29 | -- | - |
testcase_30 | -- | - |
testcase_31 | -- | - |
testcase_32 | -- | - |
testcase_33 | -- | - |
コンパイルメッセージ
復元対象のプロジェクトを決定しています... /home/judge/data/code/main.csproj を復元しました (104 ms)。 MSBuild のバージョン 17.9.6+a4ecab324 (.NET) main -> /home/judge/data/code/bin/Release/net8.0/main.dll main -> /home/judge/data/code/bin/Release/net8.0/publish/
ソースコード
using System; using System.Buffers; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics.X86; using System.Text; using YukicoderContest316.Problems; namespace YukicoderContest316.Problems { public class ProblemC : ProblemBase { public ProblemC() : base(false) { } [MethodImpl(MethodImplOptions.AggressiveOptimization)] protected override void SolveEach(IOManager io) { var s = io.ReadString(); var zeros = s.Count(c => c == '0'); var ones = s.Length - zeros; var dim0 = zeros / 2 + 1; var dim1 = ones / 2 + 1; var candidates = new List<(int, int)>[dim0, dim1]; for (int i = 0; i < dim0; i++) { for (int j = 0; j < dim1; j++) { candidates[i, j] = new List<(int, int)>(); } } var set = new SortedSet<ulong>(); var stack = new Stack<ulong>(); set.Add(Convert.ToUInt64(s, 2)); stack.Push(Convert.ToUInt64(s, 2)); while (stack.Count > 0) { var v = stack.Pop(); for (int i = 0; i < dim0; i++) { for (int j = 0; j < dim1; j++) { candidates[i, j].Clear(); } } for (int l = 0; l < s.Length; l++) { for (int r = l + 1; r <= s.Length; r++) { var mask = ((1UL << r) - 1) ^ ((1UL << l) - 1); var currentOnes = BitOperations.PopCount(v & mask); var currentZeros = r - l - currentOnes; if (currentZeros < dim0 && currentOnes < dim1 && currentZeros > 0 && currentOnes > 0) { candidates[currentZeros, currentOnes].Add((l, r)); } } } for (int z = 0; z < dim0; z++) { for (int o = 0; o < dim1; o++) { var span = candidates[z, o].AsSpan(); for (int i = 0; i < span.Length; i++) { var (l0, r0) = span[i]; for (int j = i + 1; j < span.Length; j++) { var (l1, r1) = span[j]; if (r0 <= l1) { var mask0 = ((1UL << r0) - 1) ^ ((1UL << l0) - 1); var mask1 = ((1UL << r1) - 1) ^ ((1UL << l1) - 1); var shift = l1 - l0; var a = v & ~mask0 & ~mask1; var b = (v & mask0) << shift; var c = (v & mask1) >> shift; var x = a | b | c; if (set.Add(x)) { stack.Push(x); } } } } } } } io.WriteLine(set.Count); } } } namespace YukicoderContest316 { class Program { static void Main(string[] args) { IProblem question = new ProblemC(); using var io = new IOManager(Console.OpenStandardInput(), Console.OpenStandardOutput()); question.Solve(io); } } } #region Base Class namespace YukicoderContest316.Problems { public interface IProblem { string Solve(string input); void Solve(IOManager io); } public abstract class ProblemBase : IProblem { protected bool HasMultiTestCases { get; } protected ProblemBase(bool hasMultiTestCases) => HasMultiTestCases = hasMultiTestCases; public string Solve(string input) { var inputStream = new MemoryStream(Encoding.UTF8.GetBytes(input)); var outputStream = new MemoryStream(); using var manager = new IOManager(inputStream, outputStream); Solve(manager); manager.Flush(); outputStream.Seek(0, SeekOrigin.Begin); var reader = new StreamReader(outputStream); return reader.ReadToEnd(); } public void Solve(IOManager io) { var tests = HasMultiTestCases ? io.ReadInt() : 1; for (var t = 0; t < tests; t++) { SolveEach(io); } } protected abstract void SolveEach(IOManager io); } } #endregion #region Utils namespace YukicoderContest316 { public class IOManager : IDisposable { private readonly BinaryReader _reader; private readonly StreamWriter _writer; private bool _disposedValue; private byte[] _buffer = new byte[1024]; private int _length; private int _cursor; private bool _eof; const char ValidFirstChar = '!'; const char ValidLastChar = '~'; public IOManager(Stream input, Stream output) { _reader = new BinaryReader(input); _writer = new StreamWriter(output) { AutoFlush = false }; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private char ReadAscii() { if (_cursor == _length) { _cursor = 0; _length = _reader.Read(_buffer); if (_length == 0) { if (!_eof) { _eof = true; return char.MinValue; } else { ThrowEndOfStreamException(); } } } return (char)_buffer[_cursor++]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public char ReadChar() { char c; while (!IsValidChar(c = ReadAscii())) { } return c; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public string ReadString() { var builder = new StringBuilder(); char c; while (!IsValidChar(c = ReadAscii())) { } do { builder.Append(c); } while (IsValidChar(c = ReadAscii())); return builder.ToString(); } public int ReadInt() => (int)ReadLong(); [MethodImpl(MethodImplOptions.AggressiveInlining)] public long ReadLong() { long result = 0; bool isPositive = true; char c; while (!IsNumericChar(c = ReadAscii())) { } if (c == '-') { isPositive = false; c = ReadAscii(); } do { result *= 10; result += c - '0'; } while (IsNumericChar(c = ReadAscii())); return isPositive ? result : -result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private Span<char> ReadChunk(Span<char> span) { var i = 0; char c; while (!IsValidChar(c = ReadAscii())) { } do { span[i++] = c; } while (IsValidChar(c = ReadAscii())); return span.Slice(0, i); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public double ReadDouble() => double.Parse(ReadChunk(stackalloc char[32])); [MethodImpl(MethodImplOptions.AggressiveInlining)] public decimal ReadDecimal() => decimal.Parse(ReadChunk(stackalloc char[32])); public int[] ReadIntArray(int n) { var a = new int[n]; for (int i = 0; i < a.Length; i++) { a[i] = ReadInt(); } return a; } public long[] ReadLongArray(int n) { var a = new long[n]; for (int i = 0; i < a.Length; i++) { a[i] = ReadLong(); } return a; } public double[] ReadDoubleArray(int n) { var a = new double[n]; for (int i = 0; i < a.Length; i++) { a[i] = ReadDouble(); } return a; } public decimal[] ReadDecimalArray(int n) { var a = new decimal[n]; for (int i = 0; i < a.Length; i++) { a[i] = ReadDecimal(); } return a; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Write<T>(T value) => _writer.Write(value.ToString()); [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteLine<T>(T value) => _writer.WriteLine(value.ToString()); public void WriteLine<T>(IEnumerable<T> values, char separator) { var e = values.GetEnumerator(); if (e.MoveNext()) { _writer.Write(e.Current.ToString()); while (e.MoveNext()) { _writer.Write(separator); _writer.Write(e.Current.ToString()); } } _writer.WriteLine(); } public void WriteLine<T>(T[] values, char separator) => WriteLine((ReadOnlySpan<T>)values, separator); public void WriteLine<T>(Span<T> values, char separator) => WriteLine((ReadOnlySpan<T>)values, separator); public void WriteLine<T>(ReadOnlySpan<T> values, char separator) { for (int i = 0; i < values.Length - 1; i++) { _writer.Write(values[i]); _writer.Write(separator); } if (values.Length > 0) { _writer.Write(values[^1]); } _writer.WriteLine(); } public void Flush() => _writer.Flush(); [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool IsValidChar(char c) => ValidFirstChar <= c && c <= ValidLastChar; [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool IsNumericChar(char c) => ('0' <= c && c <= '9') || c == '-'; private void ThrowEndOfStreamException() => throw new EndOfStreamException(); protected virtual void Dispose(bool disposing) { if (!_disposedValue) { if (disposing) { _reader.Dispose(); _writer.Flush(); _writer.Dispose(); } _disposedValue = true; } } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } } public static class UtilExtensions { public static bool ChangeMax<T>(ref this T value, T other) where T : struct, IComparable<T> { if (value.CompareTo(other) < 0) { value = other; return true; } return false; } public static bool ChangeMin<T>(ref this T value, T other) where T : struct, IComparable<T> { if (value.CompareTo(other) > 0) { value = other; return true; } return false; } public static void SwapIfLargerThan<T>(ref this T a, ref T b) where T : struct, IComparable<T> { if (a.CompareTo(b) > 0) { (a, b) = (b, a); } } public static void SwapIfSmallerThan<T>(ref this T a, ref T b) where T : struct, IComparable<T> { if (a.CompareTo(b) < 0) { (a, b) = (b, a); } } public static void Sort<T>(this T[] array) where T : IComparable<T> => Array.Sort(array); public static void Sort<T>(this T[] array, Comparison<T> comparison) => Array.Sort(array, comparison); } public static class CollectionExtensions { private class ArrayWrapper<T> { #pragma warning disable CS0649 public T[] Array; #pragma warning restore CS0649 } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Span<T> AsSpan<T>(this List<T> list) { return Unsafe.As<ArrayWrapper<T>>(list).Array.AsSpan(0, list.Count); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Span<T> GetRowSpan<T>(this T[,] array, int i) { var width = array.GetLength(1); return MemoryMarshal.CreateSpan(ref array[i, 0], width); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Span<T> GetRowSpan<T>(this T[,,] array, int i, int j) { var width = array.GetLength(2); return MemoryMarshal.CreateSpan(ref array[i, j, 0], width); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Span<T> GetRowSpan<T>(this T[,,,] array, int i, int j, int k) { var width = array.GetLength(3); return MemoryMarshal.CreateSpan(ref array[i, j, k, 0], width); } public static void Fill<T>(this T[] array, T value) => array.AsSpan().Fill(value); public static void Fill<T>(this T[,] array, T value) => MemoryMarshal.CreateSpan(ref array[0, 0], array.Length).Fill(value); public static void Fill<T>(this T[,,] array, T value) => MemoryMarshal.CreateSpan(ref array[0, 0, 0], array.Length).Fill(value); public static void Fill<T>(this T[,,,] array, T value) => MemoryMarshal.CreateSpan(ref array[0, 0, 0, 0], array.Length).Fill(value); } public static class SearchExtensions { struct LowerBoundComparer<T> : IComparer<T> where T : IComparable<T> { public int Compare(T x, T y) => 0 <= x.CompareTo(y) ? 1 : -1; } struct UpperBoundComparer<T> : IComparer<T> where T : IComparable<T> { public int Compare(T x, T y) => 0 < x.CompareTo(y) ? 1 : -1; } // https://trsing.hatenablog.com/entry/2019/08/27/211038 public static int GetGreaterEqualIndex<T>(this ReadOnlySpan<T> span, T inclusiveMin) where T : IComparable<T> => ~span.BinarySearch(inclusiveMin, new UpperBoundComparer<T>()); public static int GetGreaterThanIndex<T>(this ReadOnlySpan<T> span, T exclusiveMin) where T : IComparable<T> => ~span.BinarySearch(exclusiveMin, new LowerBoundComparer<T>()); public static int GetLessEqualIndex<T>(this ReadOnlySpan<T> span, T inclusiveMax) where T : IComparable<T> => ~span.BinarySearch(inclusiveMax, new LowerBoundComparer<T>()) - 1; public static int GetLessThanIndex<T>(this ReadOnlySpan<T> span, T exclusiveMax) where T : IComparable<T> => ~span.BinarySearch(exclusiveMax, new UpperBoundComparer<T>()) - 1; public static int GetGreaterEqualIndex<T>(this Span<T> span, T inclusiveMin) where T : IComparable<T> => ((ReadOnlySpan<T>)span).GetGreaterEqualIndex(inclusiveMin); public static int GetGreaterThanIndex<T>(this Span<T> span, T exclusiveMin) where T : IComparable<T> => ((ReadOnlySpan<T>)span).GetGreaterThanIndex(exclusiveMin); public static int GetLessEqualIndex<T>(this Span<T> span, T inclusiveMax) where T : IComparable<T> => ((ReadOnlySpan<T>)span).GetLessEqualIndex(inclusiveMax); public static int GetLessThanIndex<T>(this Span<T> span, T exclusiveMax) where T : IComparable<T> => ((ReadOnlySpan<T>)span).GetLessThanIndex(exclusiveMax); public static int BoundaryBinarySearch(Predicate<int> predicate, int ok, int ng) { while (Math.Abs(ok - ng) > 1) { var mid = (ok + ng) / 2; if (predicate(mid)) { ok = mid; } else { ng = mid; } } return ok; } public static long BoundaryBinarySearch(Predicate<long> predicate, long ok, long ng) { while (Math.Abs(ok - ng) > 1) { var mid = (ok + ng) / 2; if (predicate(mid)) { ok = mid; } else { ng = mid; } } return ok; } public static BigInteger BoundaryBinarySearch(Predicate<BigInteger> predicate, BigInteger ok, BigInteger ng) { while (BigInteger.Abs(ok - ng) > 1) { var mid = (ok + ng) / 2; if (predicate(mid)) { ok = mid; } else { ng = mid; } } return ok; } public static double BoundaryBinarySearch(Predicate<double> predicate, double ok, double ng, double eps = 1e-9, int loopLimit = 1000) { var count = 0; while (Math.Abs(ok - ng) > eps && count++ < loopLimit) { var mid = (ok + ng) * 0.5; if (predicate(mid)) { ok = mid; } else { ng = mid; } } return (ok + ng) * 0.5; } public static double Bisection(Func<double, double> f, double a, double b, double eps = 1e-9, int loopLimit = 100) { double mid = (a + b) / 2; var fa = f(a); if (fa * f(b) >= 0) { throw new ArgumentException("f(a)とf(b)は異符号である必要があります。"); } for (int i = 0; i < loopLimit; i++) { var fmid = f(mid); var sign = fa * fmid; if (sign < 0) { b = mid; } else if (sign > 0) { a = mid; fa = fmid; } else { return mid; } mid = (a + b) / 2; if (Math.Abs(b - a) < eps) { break; } } return mid; } } } #endregion namespace YukicoderContest316.Numerics { #region ModInt /// <summary> /// コンパイル時に決定する mod を表します。 /// </summary> /// <example> /// <code> /// public readonly struct Mod1000000009 : IStaticMod /// { /// public uint Mod => 1000000009; /// public bool IsPrime => true; /// } /// </code> /// </example> public interface IStaticMod { /// <summary> /// mod を取得します。 /// </summary> uint Mod { get; } /// <summary> /// mod が素数であるか識別します。 /// </summary> bool IsPrime { get; } } public readonly struct Mod1000000007 : IStaticMod { public uint Mod => 1000000007; public bool IsPrime => true; } public readonly struct Mod998244353 : IStaticMod { public uint Mod => 998244353; public bool IsPrime => true; } /// <summary> /// 実行時に決定する mod の ID を表します。 /// </summary> /// <example> /// <code> /// public readonly struct ModID123 : IDynamicModID { } /// </code> /// </example> public interface IDynamicModID { } public readonly struct ModID0 : IDynamicModID { } public readonly struct ModID1 : IDynamicModID { } public readonly struct ModID2 : IDynamicModID { } /// <summary> /// 四則演算時に自動で mod を取る整数型。mod の値はコンパイル時に決定している必要があります。 /// </summary> /// <typeparam name="T">定数 mod を表す構造体</typeparam> /// <example> /// <code> /// using ModInt = AtCoder.StaticModInt<AtCoder.Mod1000000007>; /// /// void SomeMethod() /// { /// var m = new ModInt(1); /// m -= 2; /// Console.WriteLine(m); // 1000000006 /// } /// </code> /// </example> public readonly struct StaticModInt<T> : IEquatable<StaticModInt<T>> where T : struct, IStaticMod { private readonly uint _v; /// <summary> /// 格納されている値を返します。 /// </summary> public int Value => (int)_v; /// <summary> /// mod を返します。 /// </summary> public static int Mod => (int)default(T).Mod; public static StaticModInt<T> Zero => new StaticModInt<T>(); public static StaticModInt<T> One => new StaticModInt<T>(1u); /// <summary> /// <paramref name="v"/> に対して mod を取らずに StaticModInt<<typeparamref name="T"/>> 型のインスタンスを生成します。 /// </summary> /// <remarks> /// <para>定数倍高速化のための関数です。 <paramref name="v"/> に 0 未満または mod 以上の値を入れた場合の挙動は未定義です。</para> /// <para>制約: 0≤|<paramref name="v"/>|<mod</para> /// </remarks> [MethodImpl(MethodImplOptions.AggressiveInlining)] public static StaticModInt<T> Raw(int v) { var u = unchecked((uint)v); Debug.Assert(u < Mod); return new StaticModInt<T>(u); } /// <summary> /// StaticModInt<<typeparamref name="T"/>> 型のインスタンスを生成します。 /// </summary> /// <remarks> /// <paramref name="v"/>が 0 未満、もしくは mod 以上の場合、自動で mod を取ります。 /// </remarks> public StaticModInt(long v) : this(Round(v)) { } private StaticModInt(uint v) => _v = v; [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint Round(long v) { var x = v % default(T).Mod; if (x < 0) { x += default(T).Mod; } return (uint)x; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static StaticModInt<T> operator ++(StaticModInt<T> value) { var v = value._v + 1; if (v == default(T).Mod) { v = 0; } return new StaticModInt<T>(v); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static StaticModInt<T> operator --(StaticModInt<T> value) { var v = value._v; if (v == 0) { v = default(T).Mod; } return new StaticModInt<T>(v - 1); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static StaticModInt<T> operator +(StaticModInt<T> lhs, StaticModInt<T> rhs) { var v = lhs._v + rhs._v; if (v >= default(T).Mod) { v -= default(T).Mod; } return new StaticModInt<T>(v); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static StaticModInt<T> operator -(StaticModInt<T> lhs, StaticModInt<T> rhs) { unchecked { var v = lhs._v - rhs._v; if (v >= default(T).Mod) { v += default(T).Mod; } return new StaticModInt<T>(v); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static StaticModInt<T> operator *(StaticModInt<T> lhs, StaticModInt<T> rhs) { return new StaticModInt<T>((uint)((ulong)lhs._v * rhs._v % default(T).Mod)); } /// <summary> /// 除算を行います。 /// </summary> /// <remarks> /// <para>- 制約: <paramref name="rhs"/> に乗法の逆元が存在する。(gcd(<paramref name="rhs"/>, mod) = 1)</para> /// <para>- 計算量: O(log(mod))</para> /// </remarks> public static StaticModInt<T> operator /(StaticModInt<T> lhs, StaticModInt<T> rhs) => lhs * rhs.Inverse(); public static StaticModInt<T> operator +(StaticModInt<T> value) => value; public static StaticModInt<T> operator -(StaticModInt<T> value) => new StaticModInt<T>() - value; public static bool operator ==(StaticModInt<T> lhs, StaticModInt<T> rhs) => lhs._v == rhs._v; public static bool operator !=(StaticModInt<T> lhs, StaticModInt<T> rhs) => lhs._v != rhs._v; public static implicit operator StaticModInt<T>(int value) => new StaticModInt<T>(value); public static implicit operator StaticModInt<T>(long value) => new StaticModInt<T>(value); /// <summary> /// 自身を x として、x^<paramref name="n"/> を返します。 /// </summary> /// <remarks> /// <para>制約: 0≤|<paramref name="n"/>|</para> /// <para>計算量: O(log(<paramref name="n"/>))</para> /// </remarks> public StaticModInt<T> Pow(long n) { Debug.Assert(0 <= n); var x = this; var r = Raw(1); while (n > 0) { if ((n & 1) > 0) { r *= x; } x *= x; n >>= 1; } return r; } /// <summary> /// 自身を x として、 xy≡1 なる y を返します。 /// </summary> /// <remarks> /// <para>制約: gcd(x, mod) = 1</para> /// </remarks> [MethodImpl(MethodImplOptions.AggressiveInlining)] public StaticModInt<T> Inverse() { if (default(T).IsPrime) { Debug.Assert(_v > 0); return Pow(default(T).Mod - 2); } else { var (g, x) = InternalMath.InvGCD(_v, default(T).Mod); Debug.Assert(g == 1); return new StaticModInt<T>(x); } } public override string ToString() => _v.ToString(); public override bool Equals(object obj) => obj is StaticModInt<T> && Equals((StaticModInt<T>)obj); public bool Equals(StaticModInt<T> other) => Value == other.Value; public override int GetHashCode() => _v.GetHashCode(); } /// <summary> /// 四則演算時に自動で mod を取る整数型。実行時に mod が決まる場合でも使用可能です。 /// </summary> /// <remarks> /// 使用前に DynamicModInt<<typeparamref name="T"/>>.Mod に mod の値を設定する必要があります。 /// </remarks> /// <typeparam name="T">mod の ID を表す構造体</typeparam> /// <example> /// <code> /// using AtCoder.ModInt = AtCoder.DynamicModInt<AtCoder.ModID0>; /// /// void SomeMethod() /// { /// ModInt.Mod = 1000000009; /// var m = new ModInt(1); /// m -= 2; /// Console.WriteLine(m); // 1000000008 /// } /// </code> /// </example> public readonly struct DynamicModInt<T> : IEquatable<DynamicModInt<T>> where T : struct, IDynamicModID { private readonly uint _v; private static Barrett bt; /// <summary> /// 格納されている値を返します。 /// </summary> public int Value => (int)_v; /// <summary> /// mod を返します。 /// </summary> public static int Mod { get => (int)bt.Mod; set { Debug.Assert(1 <= value); bt = new Barrett((uint)value); } } /// <summary> /// <paramref name="v"/> に対して mod を取らずに DynamicModInt<<typeparamref name="T"/>> 型のインスタンスを生成します。 /// </summary> /// <remarks> /// <para>定数倍高速化のための関数です。 <paramref name="v"/> に 0 未満または mod 以上の値を入れた場合の挙動は未定義です。</para> /// <para>制約: 0≤|<paramref name="v"/>|<mod</para> /// </remarks> [MethodImpl(MethodImplOptions.AggressiveInlining)] public static DynamicModInt<T> Raw(int v) { var u = unchecked((uint)v); Debug.Assert(bt != null, $"使用前に {nameof(DynamicModInt<T>)}<{nameof(T)}>.{nameof(Mod)} プロパティに mod の値を設定してください。"); Debug.Assert(u < Mod); return new DynamicModInt<T>(u); } /// <summary> /// DynamicModInt<<typeparamref name="T"/>> 型のインスタンスを生成します。 /// </summary> /// <remarks> /// <para>- 使用前に DynamicModInt<<typeparamref name="T"/>>.Mod に mod の値を設定する必要があります。</para> /// <para>- <paramref name="v"/> が 0 未満、もしくは mod 以上の場合、自動で mod を取ります。</para> /// </remarks> public DynamicModInt(long v) : this(Round(v)) { } private DynamicModInt(uint v) => _v = v; [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint Round(long v) { Debug.Assert(bt != null, $"使用前に {nameof(DynamicModInt<T>)}<{nameof(T)}>.{nameof(Mod)} プロパティに mod の値を設定してください。"); var x = v % bt.Mod; if (x < 0) { x += bt.Mod; } return (uint)x; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static DynamicModInt<T> operator ++(DynamicModInt<T> value) { var v = value._v + 1; if (v == bt.Mod) { v = 0; } return new DynamicModInt<T>(v); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static DynamicModInt<T> operator --(DynamicModInt<T> value) { var v = value._v; if (v == 0) { v = bt.Mod; } return new DynamicModInt<T>(v - 1); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static DynamicModInt<T> operator +(DynamicModInt<T> lhs, DynamicModInt<T> rhs) { var v = lhs._v + rhs._v; if (v >= bt.Mod) { v -= bt.Mod; } return new DynamicModInt<T>(v); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static DynamicModInt<T> operator -(DynamicModInt<T> lhs, DynamicModInt<T> rhs) { unchecked { var v = lhs._v - rhs._v; if (v >= bt.Mod) { v += bt.Mod; } return new DynamicModInt<T>(v); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static DynamicModInt<T> operator *(DynamicModInt<T> lhs, DynamicModInt<T> rhs) { uint z = bt.Mul(lhs._v, rhs._v); return new DynamicModInt<T>(z); } /// <summary> /// 除算を行います。 /// </summary> /// <remarks> /// <para>- 制約: <paramref name="rhs"/> に乗法の逆元が存在する。(gcd(<paramref name="rhs"/>, mod) = 1)</para> /// <para>- 計算量: O(log(mod))</para> /// </remarks> public static DynamicModInt<T> operator /(DynamicModInt<T> lhs, DynamicModInt<T> rhs) => lhs * rhs.Inverse(); public static DynamicModInt<T> operator +(DynamicModInt<T> value) => value; public static DynamicModInt<T> operator -(DynamicModInt<T> value) => new DynamicModInt<T>() - value; public static bool operator ==(DynamicModInt<T> lhs, DynamicModInt<T> rhs) => lhs._v == rhs._v; public static bool operator !=(DynamicModInt<T> lhs, DynamicModInt<T> rhs) => lhs._v != rhs._v; public static implicit operator DynamicModInt<T>(int value) => new DynamicModInt<T>(value); public static implicit operator DynamicModInt<T>(long value) => new DynamicModInt<T>(value); /// <summary> /// 自身を x として、x^<paramref name="n"/> を返します。 /// </summary> /// <remarks> /// <para>制約: 0≤|<paramref name="n"/>|</para> /// <para>計算量: O(log(<paramref name="n"/>))</para> /// </remarks> public DynamicModInt<T> Pow(long n) { Debug.Assert(0 <= n); var x = this; var r = Raw(1); while (n > 0) { if ((n & 1) > 0) { r *= x; } x *= x; n >>= 1; } return r; } /// <summary> /// 自身を x として、 xy≡1 なる y を返します。 /// </summary> /// <remarks> /// <para>制約: gcd(x, mod) = 1</para> /// </remarks> [MethodImpl(MethodImplOptions.AggressiveInlining)] public DynamicModInt<T> Inverse() { var (g, x) = InternalMath.InvGCD(_v, bt.Mod); Debug.Assert(g == 1); return new DynamicModInt<T>(x); } public override string ToString() => _v.ToString(); public override bool Equals(object obj) => obj is DynamicModInt<T> && Equals((DynamicModInt<T>)obj); public bool Equals(DynamicModInt<T> other) => Value == other.Value; public override int GetHashCode() => _v.GetHashCode(); } /// <summary> /// Fast moduler by barrett reduction /// <seealso href="https://en.wikipedia.org/wiki/Barrett_reduction"/> /// </summary> public class Barrett { public uint Mod { get; private set; } private ulong IM; public Barrett(uint m) { Mod = m; IM = unchecked((ulong)-1) / m + 1; } /// <summary> /// <paramref name="a"/> * <paramref name="b"/> mod m /// </summary> public uint Mul(uint a, uint b) { ulong z = a; z *= b; if (!Bmi2.X64.IsSupported) return (uint)(z % Mod); var x = Bmi2.X64.MultiplyNoFlags(z, IM); var v = unchecked((uint)(z - x * Mod)); if (Mod <= v) v += Mod; return v; } } public static class InternalMath { /// <summary> /// g=gcd(a,b),xa=g(mod b) となるような 0≤x<b/g の(g, x) /// </summary> /// <remarks> /// <para>制約: 1≤<paramref name="b"/></para> /// </remarks> public static (long, long) InvGCD(long a, long b) { a = SafeMod(a, b); if (a == 0) return (b, 0); long s = b, t = a; long m0 = 0, m1 = 1; long u; while (true) { if (t == 0) { if (m0 < 0) m0 += b / s; return (s, m0); } u = s / t; s -= t * u; m0 -= m1 * u; if (s == 0) { if (m1 < 0) m1 += b / t; return (t, m1); } u = t / s; t -= s * u; m1 -= m0 * u; } } public static long SafeMod(long x, long m) { x %= m; if (x < 0) x += m; return x; } } public class ModCombination<T> where T : struct, IStaticMod { readonly StaticModInt<T>[] _factorials; readonly StaticModInt<T>[] _invFactorials; public ModCombination(int max = 1000000) { if (max >= default(T).Mod) { ThrowArgumentOutOfRangeException(); } _factorials = new StaticModInt<T>[max + 1]; _invFactorials = new StaticModInt<T>[max + 1]; _factorials[0] = _factorials[1] = StaticModInt<T>.Raw(1); _invFactorials[0] = _invFactorials[1] = StaticModInt<T>.Raw(1); for (int i = 2; i < _factorials.Length; i++) { _factorials[i] = _factorials[i - 1] * StaticModInt<T>.Raw(i); } _invFactorials[^1] = _factorials[^1].Inverse(); for (int i = _invFactorials.Length - 2; i >= 0; i--) { _invFactorials[i] = _invFactorials[i + 1] * StaticModInt<T>.Raw(i + 1); } } public StaticModInt<T> Factorial(int n) => _factorials[n]; public StaticModInt<T> Permutation(int n, int k) => _factorials[n] * _invFactorials[n - k]; public StaticModInt<T> Combination(int n, int k) => _factorials[n] * _invFactorials[k] * _invFactorials[n - k]; public StaticModInt<T> CombinationWithRepetition(int n, int k) => Combination(n + k - 1, k); public void ThrowArgumentOutOfRangeException() => throw new ArgumentOutOfRangeException(); } #endregion }