結果

問題 No.3030 ミラー・ラビン素数判定法のテスト
ユーザー iwkjoseciwkjosec
提出日時 2019-05-18 21:58:20
言語 C#(csc)
(csc 3.9.0)
結果
WA  
(最新)
AC  
(最初)
実行時間 -
コード長 23,550 bytes
コンパイル時間 1,086 ms
コンパイル使用メモリ 112,128 KB
実行使用メモリ 19,840 KB
最終ジャッジ日時 2024-04-29 13:13:33
合計ジャッジ時間 4,036 ms
ジャッジサーバーID
(参考情報)
judge5 / judge1
このコードへのチャレンジ
(要ログイン)

テストケース

テストケース表示
入力 結果 実行時間
実行使用メモリ
testcase_00 WA -
testcase_01 AC 24 ms
17,536 KB
testcase_02 AC 24 ms
17,792 KB
testcase_03 AC 24 ms
17,664 KB
testcase_04 AC 459 ms
19,584 KB
testcase_05 AC 448 ms
19,456 KB
testcase_06 AC 205 ms
19,584 KB
testcase_07 AC 199 ms
19,456 KB
testcase_08 AC 203 ms
19,840 KB
testcase_09 AC 784 ms
19,456 KB
権限があれば一括ダウンロードができます
コンパイルメッセージ
Microsoft (R) Visual C# Compiler version 3.9.0-6.21124.20 (db94f4cc)
Copyright (C) Microsoft Corporation. All rights reserved.

ソースコード

diff #

using System;
using System.Collections.Generic;
using System.Linq;
using static System.Console;
using static System.Math;

using System.Numerics;
class Program
{
    static void Main()
    {
        var n = long.Parse(ReadLine());

        for (int i = 0; i < n; i++)
        {
            var x = long.Parse(ReadLine());
            WriteLine(x.ToString() + " " + (MillerRabin(x) ? "1" : "0"));
        }

    }
    static uint[] l = new uint[] { 2, 325, 9375, 28178, 450775, 9780504, 1795265022 };
    static bool MillerRabin(long n)
    {
        if (n < 2) return false;
        if (n % 2 == 0) return n == 2;
        var d = n - 1;
        var s = 0;
        while (d % 2 == 0)
        {
            s++;
            d >>= 1;
        }
        var dd = (UInt128)d;
        var nn = (UInt128)n;
        for (int i = 0; i < l.Length && l[i] < n; i++)
        {
            var a = l[i];
            var c = true;
            var x = UInt128.ModPow(a, dd, nn);
            if (x == 1) c = false;
            for (int r = 0; r < s; r++)
            {
                if (x == nn - 1) c = false;
                x = x * x % nn;
            }
            if (c) return false;
        }
        return true;
    }
}
public struct UInt128
{
    public ulong low;
    public ulong high;

    public UInt128(ulong h, ulong l)
    {
        low = l;
        high = h;
    }

    public static UInt128 operator +(UInt128 l, UInt128 r)//片方にもう片方足して返すのとin引数にして新しいインスタンス返すのどっちがいいんだろう
    {
        var s = new UInt128 { low = l.low + r.low, high = l.high + r.high };
        if (s.low < l.low) s.high++;
        return s;
    }

    public static UInt128 operator -(UInt128 l, UInt128 r)
    {
        var s = new UInt128 { low = l.low - r.low, high = l.high - r.high };
        if (s.low > l.low) s.high--;
        return s;
    }

    public static UInt128 operator *(UInt128 l, UInt128 r)//導出は n bit * n bit -> 2n bit ができないとき 上位ビット下位ビットに分割して考える
    {
        var h = l.low * r.high + l.high * r.low;

        var lll = l.low & 0xFFFFFFFF;
        var llh = l.low >> 32;
        var rll = r.low & 0xFFFFFFFF;
        var rlh = r.low >> 32;

        var a = lll * rll;
        var b = lll * rlh + llh * rll;
        var c = llh * rlh;
        if (b < lll * rlh) c += 1UL << 32;

        var lo = a + (b << 32);
        var hi = c + (b >> 32);
        if (lo < a) hi++;
        hi += h;
        return new UInt128 { low = lo, high = hi };
    }

    public static UInt128 operator /(UInt128 left, UInt128 right)
    {
        if (right.high == 0) return left / right.low;
        if (left < right) return 0;
        return left.Divide(right);
    }

    public static UInt128 operator /(UInt128 left, ulong right)
    {
        if (right == (uint)right) return left / (uint)right;
        if (left < right) return 0;
        if (right.IsPowerOf2()) return left >> right.HighBitPosition();
        return left.Divide(right);
    }

    public static UInt128 operator /(UInt128 left, uint right)
    {
        if (right == 0) throw new DivideByZeroException();
        if (left < right) return 0;
        if (left.high == 0) return left.low / right;
        return left.Divide(right);
    }
    private ulong Divide(UInt128 denominator)
    {
        var remainder = this;
        ulong result = 0;
        var denHiBit = denominator.high.HighBitPosition();
        do
        {
            var remHiBit = remainder.high.HighBitPosition();
            var diff = remHiBit - denHiBit;
            if (diff <= 3)
            {
                // div by subtraction
                do
                {
                    result++;
                    remainder -= denominator;
                } while (remainder >= denominator);

                return result;
            }

            ulong resLo;
            if (denHiBit >= 18 && diff <= 24)
            {
                // div by divide high ulongs
                resLo = remainder.high /
                        (denominator.high +
                         1
                        ); // we don't have to worry about denominator.hi being MaxValue and overflowing, because that case handled above
            }
            else
            {
                // div by shifted divide
                var denShift = denHiBit + 1;
                var den = (denominator >> denShift).low;
                if (den > remainder.high)
                {
                    // we already know division quotient will fit in a ulong
                    den++;
                    if (den == 0)
                    {
                        // handle the rare case where the shifted den + 1 overflows
                        denShift++;
                        den = 1UL << 31;
                    }

                    resLo = DivUnchecked(remainder.high, remainder.low, den);
                    resLo >>= denShift;
                }
                else
                {
                    // ensure division quotient will fit in a ulong
                    den++; // den + 1 can't overflow, because it's less then remainder.hi
                    var resHi = remainder.high / den;
                    var remHi = remainder.high - remainder.high / den * den;
                    var tempResult = new UInt128(resHi, DivUnchecked(remHi, remainder.low, den));
                    tempResult >>= denShift;
                    resLo = tempResult.low;
                }
            }

            result += resLo;
            remainder -= denominator * resLo;
        } while (remainder >= denominator);

        return result;
    }

    // denominator must be > uint.MaxValue
    private UInt128 Divide(ulong denominator)
    {
        ulong resHi = 0;
        var remHi = high;
        //ensure we can divide a UInt128 by a ulong, and have the quotient fit in a ulong
        if (remHi >= denominator)
        {
            resHi = remHi / denominator;
            remHi -= resHi * denominator;
        }

        return new UInt128(resHi, DivUnchecked(remHi, low, denominator));
    }

    // denominator must be > uint.MaxValue and > the high parameter
    private static ulong DivUnchecked(ulong high, ulong low, ulong denominator)
    {
        unchecked
        {
            ulong hiShLo, denHi, denLo, quotHi, quotLo, mid, shLoMid, shLo, rhat, left, right;

            if (denominator >> 63 != 0)
            {
                mid = high;
                shLo = low;
            }
            else
            {
                // unwind first few iterations of loop to count leading zero bits but use
                // bit fiddle if more than 3 to avoid time consuming count when there are
                // many leading zero bits. Doing a quick test of the 4 most significant
                // bit positions covers about 94% of possible ulong denominator values.
                int s;
                if (denominator >> 62 != 0) s = 1;
                else if (denominator >> 61 != 0) s = 2;
                else if (denominator >> 60 != 0) s = 3;
                else s = ((uint)(denominator >> 32)).LeadingZeroBits();
                denominator <<= s;
                mid = (high << s) | (low >> (64 - s));
                shLo = low << s;
            }

            shLoMid = mid << 32;
            hiShLo = shLo >> 32;
            shLoMid += hiShLo;
            denLo = (uint)denominator;
            denHi = denominator >> 32;
            quotHi = mid / denHi;
            if (quotHi != 0)
            {
                rhat = mid - mid / denHi * denHi;
                right = (rhat << 32) | hiShLo;
                left = quotHi * denLo;
                while (quotHi != (uint)quotHi || left > right)
                {
                    quotHi--;
                    rhat += denHi;
                    if (rhat != (uint)rhat) break;
                    right = (rhat << 32) | hiShLo;
                    left -= denLo;
                }

                shLoMid -= quotHi * denominator;
                quotHi <<= 32;
            }

            quotLo = shLoMid / denHi;
            rhat = shLoMid - shLoMid / denHi * denHi;
            right = (rhat << 32) | (uint)shLo;
            left = quotLo * denLo;
            while (quotLo != (uint)quotLo || left > right)
            {
                quotLo--;
                rhat += denHi;
                if (rhat != (uint)rhat) break;
                right = (rhat << 32) | (uint)shLo;
                left -= denLo;
            }

            return quotHi | quotLo;
        }
    }

    internal UInt128 Divide(uint denominator)
    {
        var hihi = (uint)(high >> 32);
        var reshihi = hihi == 0 ? 0 : hihi / denominator;
        var remainder = hihi == 0
            ? (uint)high
            : ((ulong)(hihi - hihi / denominator * denominator) << 32) | (uint)high;
        var reshilo = (uint)(remainder / denominator);
        var remHi = remainder - reshilo * denominator;
        remainder = (remHi << 32) | (uint)(low >> 32);
        var reslohi = (uint)(remainder / denominator);
        remHi = remainder - reslohi * denominator;
        remainder = (remHi << 32) | (uint)low;
        var reslolo = (uint)(remainder / denominator);
        return new UInt128(((ulong)reshihi << 32) | reshilo, ((ulong)reslohi << 32) | reslolo);
    }

    public static UInt128 operator %(UInt128 left, UInt128 right)
    {
        if (right.high == 0) return left % right.low;
        if (left < right) return left;
        if (right.IsPowerOf2()) return left & (right - 1);
        return left.Mod(right);
    }

    public static ulong operator %(UInt128 left, ulong right)
    {
        if (right == (uint)right) return left % (uint)right;
        if (left < right) return left.low;
        if (right.IsPowerOf2()) return left.low & (right - 1);
        return left.Mod(right);
    }

    public static uint operator %(UInt128 left, uint right)
    {
        if (right == 0) throw new DivideByZeroException();
        if (left < right) return (uint)left.low;
        if (left.high == 0) return (uint)(left.low % right);
        if (right.IsPowerOf2()) return (uint)left.low & (right - 1);
        return left.Mod(right);
    }
    // denominator must be <= this and > ulong.MaxValue
    internal UInt128 Mod(UInt128 denominator)
    {
        var remainder = this;
        var denHiBit = denominator.high.HighBitPosition();
        do
        {
            var remHiBit = remainder.high.HighBitPosition();
            var diff = remHiBit - denHiBit;
            if (diff <= 3)
            {
                // mod by subtraction
                do
                {
                    remainder -= denominator;
                } while (remainder >= denominator);

                return remainder;
            }

            ulong resLo, den;
            if (denHiBit >= 18 && diff <= 24)
            {
                // mod by divide high ulongs
                resLo = remainder.high /
                        (denominator.high +
                         1
                        ); // we don't have to worry about denominator.hi being MaxValue and overflowing, because that case handled above
            }
            else
            {
                // mod by shifted divide
                var denShift = denHiBit + 1;
                den = (denominator >> denShift).low;
                if (den > remainder.high)
                {
                    // we already know division quotient will fit in a ulong
                    den++;
                    if (den == 0)
                    {
                        // handle the rare case where the shifted den + 1 overflows
                        denShift++;
                        den = 1UL << 31;
                    }

                    resLo = DivUnchecked(remainder.high, remainder.low, den);
                    resLo >>= denShift;
                }
                else
                {
                    // ensure division quotient will fit in a ulong
                    den++; // den + 1 can't overflow, because it's less then remainder.hi
                    var resHi = remainder.high / den;
                    var remHi = remainder.high - remainder.high / den * den;
                    var tempResult = new UInt128(resHi, DivUnchecked(remHi, remainder.low, den));
                    tempResult >>= denShift;
                    resLo = tempResult.low;
                }
            }

            remainder -= denominator * resLo;
        } while (remainder >= denominator);

        return remainder;
    }

    // denominator must be > uint.MaxValue
    internal ulong Mod(ulong denominator)
    {
        unchecked
        {
            var high = this.high;
            var low = this.low;
            //ensure we can divide a UInt128 by a ulong, and have the quotient fit in a ulong
            if (high >= denominator) high -= high / denominator * denominator;
            if (high == 0) return low - low / denominator * denominator;

            ulong hiShLo, denHi, denLo, quotHi, quotLo, mid, shLoMid, shLo, rhat, left, right;
            int shift;
            if (denominator >> 63 != 0)
            {
                mid = high;
                shLo = low;
                shift = 0;
            }
            else
            {
                // unwind first few iterations of loop to count leading zero bits but use
                // bit fiddle if more than 3 to avoid time consuming count when there are
                // many leading zero bits. Doing a quick test of the 4 most significant
                // bit positions covers about 94% of possible ulong denominator values.
                if (denominator >> 62 != 0) shift = 1;
                else if (denominator >> 61 != 0) shift = 2;
                else if (denominator >> 60 != 0) shift = 3;
                else shift = ((uint)(denominator >> 32)).LeadingZeroBits();
                denominator <<= shift;
                mid = (high << shift) | (low >> (64 - shift));
                shLo = low << shift;
            }

            shLoMid = mid << 32;
            hiShLo = shLo >> 32;
            shLoMid += hiShLo;
            denLo = (uint)denominator;
            denHi = denominator >> 32;
            quotHi = mid / denHi;
            if (quotHi != 0)
            {
                rhat = mid - mid / denHi * denHi;
                right = (rhat << 32) | hiShLo;
                left = quotHi * denLo;
                while (quotHi != (uint)quotHi || left > right)
                {
                    quotHi--;
                    rhat += denHi;
                    if (rhat != (uint)rhat) break;
                    right = (rhat << 32) | hiShLo;
                    left -= denLo;
                }

                shLoMid -= quotHi * denominator;
            }

            quotLo = shLoMid / denHi;
            rhat = shLoMid - shLoMid / denHi * denHi;
            right = (rhat << 32) | (uint)shLo;
            left = quotLo * denLo;
            while (quotLo != (uint)quotLo || left > right)
            {
                quotLo--;
                rhat += denHi;
                if (rhat != (uint)rhat) break;
                right = (rhat << 32) | (uint)shLo;
                left -= denLo;
            }

            return ((shLoMid << 32) + ((uint)shLo - quotLo * denominator)) >> shift;
        }
    }

    internal uint Mod(uint denominator)
    {
        var hihi = (uint)(high >> 32);
        var remainder = hihi == 0
            ? (uint)high
            : ((ulong)(hihi - hihi / denominator * denominator) << 32) | (uint)high;
        var remHi = (remainder % denominator) << 32;
        remainder = remHi | (uint)(low >> 32);
        remHi = (remainder % denominator) << 32;
        remainder = remHi | (uint)low;
        return (uint)(remainder % denominator);
    }

    public static UInt128 operator &(UInt128 l, UInt128 r)
    {
        return new UInt128 { low = l.low & r.low, high = l.high & r.high };
    }

    public static UInt128 operator |(UInt128 l, UInt128 r)
    {
        return new UInt128 { low = l.low | r.low, high = l.high | r.high };
    }

    public static UInt128 operator ^(UInt128 l, UInt128 r)
    {
        return new UInt128 { low = l.low ^ r.low, high = l.high ^ r.high };
    }

    public static UInt128 operator <<(UInt128 value, int shift)
    {
        if (shift == 0) return value;
        return shift >= 64
            ? new UInt128(value.low << (shift - 64), 0UL)
            : new UInt128((value.high << shift) | (value.low >> (64 - shift)), value.low << shift);
    }

    public static UInt128 operator >>(UInt128 value, int shift)
    {
        if (shift == 0) return value;
        return shift >= 64
            ? new UInt128(0UL, value.high >> (shift - 64))
            : new UInt128(value.high >> shift, (value.low >> shift) | (value.high << (64 - shift)));
    }

    public static bool operator ==(UInt128 l, UInt128 r)
    {
        return l.low == r.low && l.high == r.high;
    }

    public static bool operator !=(UInt128 l, UInt128 r)
    {
        return l.low != r.low || l.high != r.high;
    }

    public static bool operator >(UInt128 l, UInt128 r)
    {
        if (l.high > r.high) return true;
        if (l.high < r.high) return false;
        return l.low > r.low;
    }

    public static bool operator <(UInt128 l, UInt128 r)
    {
        if (l.high < r.high) return true;
        if (l.high > r.high) return false;
        return l.low < r.low;
    }

    public static bool operator >=(UInt128 l, UInt128 r)
    {
        if (l.high > r.high) return true;
        if (l.high == r.high && l.low >= r.low) return true;
        return false;
    }

    public static bool operator <=(UInt128 l, UInt128 r)
    {
        if (l.high < r.high) return true;
        if (l.high == r.high && l.low <= r.low) return true;
        return false;
    }

    public static UInt128 operator ~(UInt128 v)
    {
        v.low = ~v.low;
        v.high = ~v.high;
        return v;
    }

    public static UInt128 operator +(UInt128 v)
    {
        return v;
    }

    public static UInt128 operator ++(UInt128 v)
    {
        return v + 1;
    }

    public static UInt128 operator --(UInt128 v)
    {
        return v - 1;
    }

    public static explicit operator UInt128(sbyte v)
    {
        return new UInt128 { low = (ulong)v };
    }

    public static implicit operator UInt128(byte v)
    {
        return new UInt128 { low = v };
    }

    public static explicit operator UInt128(short v)
    {
        return new UInt128 { low = (ulong)v };
    }

    public static implicit operator UInt128(ushort v)
    {
        return new UInt128 { low = v };
    }

    public static explicit operator UInt128(int v)
    {
        return new UInt128 { low = (ulong)v };
    }

    public static implicit operator UInt128(uint v)
    {
        return new UInt128 { low = v };
    }

    public static explicit operator UInt128(long v)
    {
        return new UInt128 { low = (ulong)v };
    }

    public static implicit operator UInt128(ulong v)
    {
        return new UInt128 { low = v };
    }

    public static explicit operator UInt128(BigInteger v)
    {
        var ret = new UInt128 { low = (ulong)v };
        v >>= 64;
        ret.high = (ulong)v;
        return ret;
    }

    public static explicit operator sbyte(UInt128 v)
    {
        return (sbyte)(v.low & 0x7F);
    }

    public static explicit operator byte(UInt128 v)
    {
        return (byte)v.low;
    }

    public static explicit operator short(UInt128 v)
    {
        return (short)(v.low & 0x7FFF);
    }

    public static explicit operator ushort(UInt128 v)
    {
        return (ushort)v.low;
    }

    public static explicit operator int(UInt128 v)
    {
        return (int)v.low & int.MaxValue;
    }

    public static explicit operator uint(UInt128 v)
    {
        return (uint)v.low;
    }

    public static explicit operator long(UInt128 v)
    {
        return (long)v.low & long.MaxValue;
    }

    public static explicit operator ulong(UInt128 v)
    {
        return v.low;
    }

    public static implicit operator BigInteger(UInt128 v)
    {
        return (new BigInteger(v.high) << 64) | v.low;
    }

    public static UInt128 ModPow(UInt128 x, UInt128 n, UInt128 m)
    {
        var res = (UInt128)1;
        while (n != 0)
        {
            if ((n & 1) == 1) res = res * x % m;
            x = x * x % m;
            n >>= 1;
        }
        return res;
    }

    public static UInt128 Parse(string s)// -0を許容して -nはovfとして扱いたい 要はuint,ulongと挙動をそろえたい
    {
        if (s is null) throw new ArgumentNullException();
        s = s.Trim();
        if (s.Length == 0) throw new FormatException();
        if (s[0] == '+')
        {
            if (s.Length == 1) throw new FormatException();
            else s = s.Substring(1);
        }
        var a = -1;
        for (int i = 0; i < s.Length; i++)
        {
            if (s[i] != '0')
            {
                a = i;
                break;
            }
        }
        if (a == -1) return new UInt128 { low = 0, high = 0 };
        s = s.Substring(a);
        if (s.Length > 39) throw new FormatException();
        for (int i = 0; i < s.Length; i++)
        {
            if ((uint)(s[i] - '0') > '9') throw new FormatException();
        }
        if (s.Length == 39 && s.CompareTo("340282366920938463463374607431768211456") != 1) throw new OverflowException();
        var ret = new UInt128();
        for (int i = 0; i < s.Length; i++)
        {
            ret *= 10;
            ret += (uint)(s[i] - '0');
        }
        return ret;
    }

    public override string ToString()
    {
        if (this == 0) return "0";
        var a = this;
        var ret = new List<char>(40);
        while (a != 0)
        {
            ret.Add((char)((int)(a % 10) + '0'));
            a /= 10;
        }
        ret.Reverse();
        return new string(ret.ToArray());
    }

    public override bool Equals(object obj)
    {
        return base.Equals(obj);
    }

    public override int GetHashCode()
    {
        return base.GetHashCode();
    }
}

internal static class SoftWxNumerics
{
    private static readonly byte[] DeBruijnLsBsSet =
    {
            0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30,
            8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31
        };

    public static int HighBitPosition(this uint value)
    {
        if (value == 0) return -1;
        value |= value >> 1;
        value |= value >> 2;
        value |= value >> 4;
        value |= value >> 8;
        return DeBruijnLsBsSet[unchecked((value | value >> 16) * 0x07c4acddu) >> 27];
    }

    public static int HighBitPosition(this ulong value)
    {
        uint high = (uint)(value >> 32);
        return high != 0 ? 32 + HighBitPosition(high) : HighBitPosition((uint)value);
    }

    public static int LeadingZeroBits(this uint value)
    {
        return unchecked(31 - HighBitPosition(value));
    }

    public static bool IsPowerOf2(this uint value)
    {
        return (value & unchecked(value - 1)) == 0 && value != 0;
    }

    public static bool IsPowerOf2(this ulong value)
    {
        return (value & unchecked(value - 1UL)) == 0 && value != 0;
    }

    public static bool IsPowerOf2(this UInt128 value)
    {
        return (value & value - 1) == 0 && value != 0;
    }
}
0