結果
| 問題 |
No.981 一般冪乗根
|
| ユーザー |
keymoon
|
| 提出日時 | 2020-02-03 17:32:33 |
| 言語 | C#(csc) (csc 3.9.0) |
| 結果 |
WA
|
| 実行時間 | - |
| コード長 | 7,394 bytes |
| コンパイル時間 | 1,097 ms |
| コンパイル使用メモリ | 119,304 KB |
| 実行使用メモリ | 56,468 KB |
| 最終ジャッジ日時 | 2024-10-09 14:10:51 |
| 合計ジャッジ時間 | 158,031 ms |
|
ジャッジサーバーID (参考情報) |
judge4 / judge3 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| other | WA * 25 TLE * 1 -- * 18 |
コンパイルメッセージ
Microsoft (R) Visual C# Compiler version 3.9.0-6.21124.20 (db94f4cc) Copyright (C) Microsoft Corporation. All rights reserved.
ソースコード
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using static System.Math;
using MethodImplAttribute = System.Runtime.CompilerServices.MethodImplAttribute;
using MethodImplOptions = System.Runtime.CompilerServices.MethodImplOptions;
public static class P
{
public static void Main()
{
Console.SetOut(new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = false });
string line;
line = Console.ReadLine();
//Assert(Regex.IsMatch(line, @"^\d+$"));
var t = int.Parse(line);
//Assert(1 <= t && t <= 1000);
for (int i = 0; i < t; i++)
{
line = Console.ReadLine();
//Assert(Regex.IsMatch(line, @"^\d+ \d+ \d+$"));
var pka = line.Split().Select(int.Parse).ToArray();
var p = pka[0];
var k = pka[1];
var a = pka[2];
//Assert(2 <= k && k <= 1000);
//Assert(2 <= p && p <= 1000000007);
//Assert(1 <= a && a < p);
Solve(p, k, a);
}
Console.Out.Flush();
}
static void Solve(int p, int k, int a)
{
ModInt.Mod = p;
var step = GCD(k, p - 1);
if (Power(a, (p - 1) / step) != 1) { Console.WriteLine(-1); return; }
//有限体Z/pZの原始根gを一つ求め、
var g = GetPrimitiveRoot(p);
//そのgについてのaの指数を求める。
var aIndex = Log(a, g);
//これによって、Z/(p-1)Z上でのa/kを求める問題に帰着できる
//∵x^k≡a(mod p) ⇔ (g^xInd)^k≡(g^aInd)(mod p) ⇔ (xInd)*k≡aInd(mod p-1)
//x=a/kとすると、kx=aより、aはZ/(p-1)Z上でik(i∈ℕ)と等しい必要がある。
//これは、Z/(p-1)Z上において指数がgcd(k, p - 1)で割り切れる要素のみで構成される群の要素と等しい。
//よって、stepを生成元として(p-1)を法とする加法によって生成される巡回群の位数orderは、
var order = (p - 1) / step;
//まず、aがその巡回群に乗っていない場合、除算は不可能。
//Z/orderZ上でのa,kの指数はそれぞれ、
var aIndexOnZOrderZ = aIndex / step;
var kIndexOnZOrderZ = k / step;
//ここで、Z/orderZ上でのa/k=xの指数は、
var xIndex = (aIndexOnZOrderZ * GetInverse(kIndexOnZOrderZ, order)) % order;
//Z/orderZはZ/(p-1)Zの部分群なので、Z/(p-1)Zにおいても指数は等しい。よって、
Console.WriteLine(Power(g, xIndex));
}
static int GetPrimitiveRoot(long m)
{
var subgroupOrders = new List<long>();
var order = m - 1;
if ((order & 1) == 0)
{
while ((order & 1) == 0) order >>= 1;
subgroupOrders.Add((m - 1) / 2);
}
for (long i = 3; i * i <= order; i += 2)
if (order % i == 0)
{
while (order % i == 0) order /= i;
subgroupOrders.Add((m - 1) / i);
}
if (order != 1) subgroupOrders.Add((m - 1) / order);
for (int g = 2; g < m; g++)
{
if (subgroupOrders.Any(x => Power(g, x) == 1)) continue;
return g;
}
throw new Exception();
}
static int Log(ModInt a, ModInt b)
{
const int PACKET_SIZE = 65536;
ModInt currentInv = 1;
Dictionary<ModInt, int> babySteps = new Dictionary<ModInt, int>();
for (int i = 0; i < PACKET_SIZE; i++)
{
if (babySteps.ContainsKey(currentInv))
{
if (babySteps.ContainsKey(a)) return babySteps[a];
else return -1;
}
babySteps.Add(currentInv, i);
currentInv *= b;
}
ModInt singleGiantStepInv = currentInv;
currentInv = 1;
for (int i = 0; i < ModInt.Mod; i += PACKET_SIZE)
{
var babyStep = a * currentInv;
if (babySteps.ContainsKey(babyStep)) return i + babySteps[babyStep];
currentInv *= singleGiantStepInv;
}
return -1;
}
static ModInt Power(ModInt n, long m)
{
ModInt pow = n;
ModInt res = 1;
while (m > 0)
{
if ((m & 1) == 1) res *= pow;
pow *= pow;
m >>= 1;
}
return res;
}
static void Assert(bool cond)
{
if (!cond) throw new Exception();
}
static long GCD(long a, long b)
{
while (true)
{
if (b == 0) return a;
a %= b;
if (a == 0) return b;
b %= a;
}
}
static long GetInverse(long a, long MOD)
{
long div, p = MOD, x1 = 1, y1 = 0, x2 = 0, y2 = 1;
while (true)
{
if (p == 1) return x2 + MOD; div = a / p; x1 -= x2 * div; y1 -= y2 * div; a %= p;
if (a == 1) return x1 + MOD; div = p / a; x2 -= x1 * div; y2 -= y1 * div; p %= a;
}
}
}
struct ModInt
{
public static int Mod
{
get
{
return MOD;
}
set
{
MOD = value;
POSITIVIZER = (long)MOD << 31;
}
}
static int MOD = 1000000007;
static long POSITIVIZER = ((long)MOD) << 31;
long Data;
public ModInt(long data) { if ((Data = data % MOD) < 0) Data += MOD; }
public static implicit operator long(ModInt modInt) => modInt.Data;
public static implicit operator ModInt(long val) => new ModInt(val);
public static ModInt operator +(ModInt a, int b) => new ModInt() { Data = (a.Data + b + POSITIVIZER) % MOD };
public static ModInt operator +(ModInt a, long b) => new ModInt(a.Data + b);
public static ModInt operator +(ModInt a, ModInt b) { long res = a.Data + b.Data; return new ModInt() { Data = res >= MOD ? res - MOD : res }; }
public static ModInt operator -(ModInt a, int b) => new ModInt() { Data = (a.Data - b + POSITIVIZER) % MOD };
public static ModInt operator -(ModInt a, long b) => new ModInt(a.Data - b);
public static ModInt operator -(ModInt a, ModInt b) { long res = a.Data - b.Data; return new ModInt() { Data = res < 0 ? res + MOD : res }; }
public static ModInt operator *(ModInt a, int b) => new ModInt(a.Data * b);
public static ModInt operator *(ModInt a, long b) => a * new ModInt(b);
public static ModInt operator *(ModInt a, ModInt b) => new ModInt() { Data = a.Data * b.Data % MOD };
public static ModInt operator /(ModInt a, ModInt b) => new ModInt() { Data = a.Data * GetInverse(b) % MOD };
public override string ToString() => Data.ToString();
static long GetInverse(long a)
{
long div, p = MOD, x1 = 1, y1 = 0, x2 = 0, y2 = 1;
while (true)
{
if (p == 1) return x2 + MOD; div = a / p; x1 -= x2 * div; y1 -= y2 * div; a %= p;
if (a == 1) return x1 + MOD; div = p / a; x2 -= x1 * div; y2 -= y1 * div; p %= a;
}
}
public override bool Equals(object obj) => ((ModInt)obj).Data == Data;
public override int GetHashCode() => (int)Data;
}
keymoon