This documentation is automatically generated by online-judge-tools/verification-helper
$\mathbb{Z}$ を整数環とし, $m$ を正の整数とする. このとき, $m \mathbb{Z}$ は $\mathbb{Z}$ のイデアルであるから, 剰余環 $\mathbb{Z}/m \mathbb{Z}$ を考えることができる.
$x \in \mathbb{Z}$ の属する類を $[x], [x]_m$ と書くことにする. このとき, $x,y \in \mathbb{Z}$ に対して, 以下は同値になる.
よって, 各整数の余りを考えることにより,
\[\mathbb{Z}/m\mathbb{Z}=\{[0], [1], \dots, [m-1] \}\]となることがわかる.
この剰余環は次の和と積によって可換環になる.
$[x] \in \mathbb{Z}/m \mathbb{Z}$ が可逆元になることの必要十分条件は $x,m$ が互いに素になることである.
よって, $\mathbb{Z}/m\mathbb{Z}$ が体になることの必要十分条件は $m$ が素数であることである.
$m$ の素因数分解を $m=p_1^{e_1} \dots p_k^{e_k}$ とする. このとき, 中国剰余定理から
\[\displaystyle \mathbb{Z}/m\mathbb{Z} \simeq \prod_{i=1}^k (\mathbb{Z}/p_i^{e_i} \mathbb{Z})\]である. このとき, 同型写像 $\varphi$ として
\[\varphi([x]):=([x \bmod{p_1^{e_1}}]_{p_1^{e_1}}, \dots, [x \bmod{p_k^{e_k}}]_{p_k^{e_k}})\]が挙げられる.
$x \in \mathbb{Z}$ としたとき,
\[x \equiv a \pmod{m} \cdots (\spadesuit), \quad x \equiv b \pmod{n} \cdots (\heartsuit)\]を共にみたすような整数 $x$ の特徴づけをする (中国剰余定理の節における $\varphi$ の逆関数の構成).
$(\spadesuit), (\heartsuit)$ を書き換えると,
となることから, 合わせると
ということになる. 式変形をし, $d:=b-a$ とおくと,
となる. このような整数 $p,q$ が存在することの必要十分条件は $d$ が $g:=\gcd(m,n)$ の倍数であることである.
以降, $d$ が $\gcd(m,n)$ の倍数であるとし, 整数 $m’,n’,d’$ をそれぞれ
\[m=gm', \quad n=gn', \quad d=gd'\]とすると, $(\diamondsuit)$ は $mp \equiv d \pmod{n}$ と同値である.
$m,n,d$ は全て $g$ の倍数であるから, $m’p \equiv d’ \pmod{n’}$ と同値になる.
$m’, n’$ は互いに素であるので, $m’ k \equiv 1 \pmod{n’}$ となる整数 $k$ が存在する. これを両辺にかけることにより,
\[p \equiv dk \pmod{n'}\]を得る.
これを $x=a+mp$ に代入することによって, $mn’=\dfrac{mn}{\gcd(m,n)}=\operatorname{lcm}(m,n)$ であるから,
\[x \equiv a+mdk \pmod{\operatorname{lcm}(m,n)}\]を得る. 逆に, これを満たす整数 $x$ は全て $(\spadesuit), (\heartsuit)$ を満たす.
$ax \equiv b \pmod{m}$ を満たす整数 $x$ の特徴づけを行う.
まず, $b$ が $g:=\gcd(a,m)$ の倍数でない場合はこのような整数 $x$ は存在しない. 整数 $a’, b’, m’$ は $a=ga’, b=gb’, m=gm’$ を満たすようにとる.
このとき, $a’, m’$ は互いに素なので, $a’k \equiv 1 \pmod{m’}$ なる整数 $k$ が存在する. よって,
\[ax \equiv b \pmod{m} \iff x \equiv bk \pmod{m'}\]となる.
$p$ を素数とする. 整数 $a \in \mathbb{Z}$ におけるルジャンドル記号 $\displaystyle \left( \dfrac{a}{p} \right)$ を
\[\left( \dfrac{a}{p} \right) \equiv a^{(p-1)/2} \pmod{p}, \quad \left( \dfrac{a}{p} \right) \in \{-1,0,1\}\]をみたす (唯一の) 整数と定義する (well-defindness はフェルマーの小定理より従う).
このとき,
よって, $a$ が $p$ を法にして平方剰余であることと, $\left( \dfrac{a}{p} \right) \neq -1$ であることは同値になる.
$[a] \in \mathbb{Z}/m \mathbb{Z}$ に対して, $[a]^n=[1]$ となる 正の整数 $n$ が存在するか? 存在するならばその $n$ の最小値 $\operatorname{ord} [a]$を求める.
まず, 存在性については $[a]^n=[1]$ が存在することと, $[a]$ が可逆である. つまり, $a,m$ が互いに素になることが同値になる.
実際, $[a]^n=[1]$ となる正の整数 $n$ が存在するならば, $[a]^{-1}=[a]^{n-1}$ であるから, $[a]$ は可逆である. 一方で, $[a]$ が可逆であるとき, $[a]^0, [a]^1, \dots, [a]^m$ には鳩ノ巣原理から, $0 \leq i<j \leq m$ で $[a]^i=[a]^j$ となるようなものが存在する. $[a]$ は可逆と仮定しているので, $[a]^{j-i}=[1]$ である.
可逆元 $[a]$ に対して, 積に関して生成される $\left \langle [a] \right \rangle$ は $(\mathbb{Z}/m\mathbb{Z})^\times$ の部分群である. このとき, ラグランジュの定理から次のことが従う.
また, この2つの群の位数について,
が成り立つ.
以上のことから,
\[\operatorname{ord} [a]=\min \{d \mid 1 \leq d \leq m, d~|~\varphi(m), [a]^d=1 \}\]となる.
次のような定理がある.
原始根の存在定理
$p$ を素数とする. このとき, $\mathbb{Z}/p \mathbb{Z}$ には位数 $(p-1)$ の元が存在する.
このような元のことを原始根という.
$p$ を素数とすると, 任意の $p$ の倍数ではない整数 $a$ に対して, $[x]^{p-1}=[1]$ である.
$[g] \in (\mathbb{Z}/p \mathbb{Z})^\times$ が原始根であることの必要十分条件は任意の $(p-1)$ の素因数 $q$ に対して, $[a]^{(p-1)/q} \neq [1]$ となることである.
class Modulo:
__slots__ = ("_a", "_n")
@property
def a(self) -> int:
return self._a
@property
def n(self) -> int:
return self._n
def __init__(self, a: int, n: int, mode: bool = True):
if mode:
a %= n
self._a = a
self._n = n
def __str__(self) -> str:
return f"{self.a} (mod {self.n})"
def __repr__(self) -> str:
return f"{self.__class__.__name__}({self.a}, {self.n})"
#+,-
def __pos__(self) -> "Modulo":
return self
def __neg__(self) -> "Modulo":
return Modulo(self.n - self.a, self.n, False) if self.a else Modulo(0, self.n, False)
#等号,不等号
def __eq__(self, other: "Modulo") -> bool:
if isinstance(other, Modulo):
return (self.a == other.a) and (self.n == other.n)
elif isinstance(other, int):
return (self.a - other) % self.n == 0
def __neq__(self, other: "Modulo") -> bool:
return not(self == other)
def __le__(self, other: "Modulo") -> bool:
a, p = self.a, self.n
b, q = other.a, other.n
return (a - b) % q == 0 and p % q == 0
def __ge__(self, other: "Modulo") -> bool:
return other <= self
def __lt__(self, other: "Modulo") -> bool:
return (self <= other) and (self != other)
def __gt__(self, other: "Modulo") -> bool:
return (self >= other) and (self != other)
def __contains__(self, val) -> bool:
return val % self.n == self.a
#加法
def __add__(self, other: "Modulo") -> "Modulo":
if isinstance(other,Modulo):
assert self.n==other.n, "異なる法同士の演算です."
y=other.a
elif isinstance(other,int):
y=other%self.n
b=self.a+y
if self.n<=b:
b-=self.n
return Modulo(b,self.n, False)
def __radd__(self, other: "Modulo") -> "Modulo":
if isinstance(other,int):
b=self.a+(other%self.n)
if b>=self.n:
b-=self.n
return Modulo(b,self.n, False)
def __iadd__(self, other: "Modulo") -> "Modulo":
if isinstance(other,Modulo):
assert self.n==other.n, "異なる法同士の演算です."
y=other.a
elif isinstance(other,int):
y=other%self.n
self.a+=y
if self.a>=self.n:
self.a-=self.n
return self
#減法
def __sub__(self, other: "Modulo") -> "Modulo":
if isinstance(other,Modulo):
assert self.n==other.n, "異なる法同士の演算です."
y=other.a
elif isinstance(other,int):
y=other%self.n
b=self.a-y
if b<0:
b+=self.n
return Modulo(b,self.n, False)
def __rsub__(self, other: "Modulo") -> "Modulo":
if isinstance(other,int):
b=other%self.n-self.a
if b<0:
b+=self.n
return Modulo(b,self.n, False)
def __isub__(self, other: "Modulo") -> "Modulo":
if isinstance(other,Modulo):
assert self.n==other.n, "異なる法同士の演算です."
y=other.a
elif isinstance(other,int):
y=other%self.n
self.a-=y
if self.a<0:
self.a+=self.n
return self
#乗法
def __mul__(self, other: "Modulo") -> "Modulo":
if isinstance(other,Modulo):
assert self.n==other.n, "異なる法同士の演算です."
y=other.a
elif isinstance(other,int):
y=other%self.n
return Modulo((self.a*y)%self.n, self.n, False)
def __rmul__(self, other: "Modulo") -> "Modulo":
if isinstance(other,int):
return Modulo((self.a*other)%self.n, self.n, False)
def __imul__(self, other: "Modulo") -> "Modulo":
if isinstance(other,Modulo):
assert self.n==other.n, "異なる法同士の演算です."
y=other.a
elif isinstance(other,int):
y=other%self.n
self.a*=y
self.a%=self.n
return self
#Modulo逆数
def inverse(self) -> "Modulo":
return self.modulo_inverse()
def modulo_inverse(self) -> "Modulo":
try:
return Modulo(pow(self.a, -1, self.n), self.n, False)
except ValueError:
raise ValueError(f"{self} の逆数が存在しません") from None
#除法
def __truediv__(self,other) -> "Modulo":
return self*(other.modulo_inverse())
def __rtruediv__(self,other) -> "Modulo":
return other*(self.modulo_inverse())
#累乗
def __pow__(self, other: int) -> "Modulo":
if isinstance(other, int):
return Modulo(pow(self.a, other, self.n), self.n, False)
else:
b,n=other.a,other.n
assert pow(self.a,n,self.n)==1, "矛盾なく定義できません."
return self**b
"""
初等的
"""
def Modulo_Inverse_List(M:int,K:int):
"""
1^(-1), 2^(-1), ... , K^(-1) (mod N) のリストを出力する.
[入力]
M,K:整数
M>0, K>=1
K=min(M-1,K) に変換される.
[出力]
長さ K+1 のリスト F
k=1,2,...,K に対して, F[k]=k^(-1) mod M
また, k^(-1) mod M が存在しない場合, F[k]=None
"""
assert M>0 and K>=1
if K==None:
K=M-1
K=min(K,M-1)
F=[None,Modulo(1,M)]
for k in range(2,K+1):
q,r=divmod(M,k)
if F[r]!=None:
F.append(-q*F[r])
else:
F.append(None)
return F
#細分化
def Subdivision(X:Modulo,M:int):
""" X をx (mod M) の形に細分化する.
X.n | Mでなくてはならない.
"""
assert M%X.n==0,"X.n | M ではありません."
k=M//X.n
return [Modulo(X.n*i+X.a,M) for i in range(k)]
#退化
def Degenerate(X:Modulo, M:int):
""" X の情報を退化させる. X=x (mod N) であるとき, mod M での情報に退化させる.
M | X.n でなくてはならない.
"""
assert X.n%M==0,"M | X.n ではありません."
return Modulo(X.a%M,M)
def Chinese_Remainder(X: Modulo):
""" 中国剰余定理により, Xを分解する.
"""
Y=[]
a,N=X.a,X.n
e=(N&(-N)).bit_length()-1
if e>0:
N>>=e
Y.append(Modulo(a,1<<e))
e=0
while N%3==0:
e+=1
N//=3
if e>0:
Y.append(Modulo(a,pow(3,e)))
flag=0
p=5
while p*p<=N:
if N%p==0:
e=0
while N%p==0:
e+=1
N//=p
Y.append(Modulo(a,pow(p,e)))
p+=2+2*flag
flag^=1
if N>1:
Y.append(Modulo(a,N))
return Y
"""
線形合同方程式関連
"""
def Modulo_Composite(*X: Modulo) -> Modulo:
""" N 個の Modulo の共通部分を求める.
Returns:
Modulo: 共通部分
"""
def composite(p: Modulo, q: Modulo) -> Modulo | None:
""" 2つの等式 x ≡ p.a (mod p.n), x ≡ q.a (mod q.n) をともに満たす x を全て求める.
Args:
p (Modulo):
q (Modulo):
Returns:
Modulo | None: 条件を満たすことが必要十分になる Modulo. 存在しない場合は None
"""
from math import gcd
a, n = p.a, p.n
b, m = q.a, q.n
d = b - a
g = gcd(n, m)
if d % g:
return None
n //= g
m //= g
d //= g
s = pow(n, -1, m)
return Modulo(a + (n * g) * d * s, n * m *g)
res = Modulo(0, 1)
for a in X:
if (res := composite(res, a)) is None:
break
return res
def Is_Included(X: Modulo, Y: Modulo):
""" X を全て満たす整数は Y を全て満たすか?
X,Y: Modulo
"""
a,p=X.a,X.n
b,q=Y.a,Y.n
return (a-b)%q==0 and p%q==0
#拡張Euclidの互除法
def Extended_Euclid(a: int, b: int) -> tuple[int, int, int]:
""" a x + b y = gcd(a, b) を満たす整数の組 (x, y) を求める.
Args:
a (int): 整数
b (int): 整数
Returns:
tuple[int, int, int]: (x, y, g) は a x + b y = g を満たす.
"""
from math import gcd
g = gcd(a, b)
if g == 0:
return (0, 0, 0)
x = pow(a//g, -1, b//g)
y = - (a*x-g) // b
return (x, y, g)
#1次合同方程式を解く
def First_Order_Congruent_Equation(a: int, b: int, m: int) -> Modulo:
""" 1次合同方程式 a x ≡ b (mod m) を求める.
Args:
a (int):
b (int):
m (int): m != 0
Returns:
Modulo: 条件を満たす X が存在しない場合は None.
"""
from math import gcd
if m == 0:
raise ValueError
g = gcd(a, m)
# 存在確認
if b % g:
return None
a, b, m = a // g, b // g, m // g
c = pow(a, -1, m)
return Modulo(b * c, m)
#1次連立合同方程式を解く
def First_Order_Simultaneous_Congruent_Equation(*X: tuple[int, int, int]) -> Modulo:
""" 1 次合同方程式 a_i x ≡ b_i (mod m_i) を求める.
Args:
X (list[tuple[int, int, int]]): (a, b, m) の形のタプル. (a, b, m) は ax ≡ b (mod m) を意味する.
Returns:
Modulo: 解
"""
equations = []
for (a, b, m) in X:
t = First_Order_Congruent_Equation(a, b, m)
if t is None:
break
equations.append(t)
return Modulo_Composite(*equations)
"""
総和
"""
def Geometric_Sum(X, L, R):
""" sum_{i=L}^R X^i を求める.
X: modulo
0<=L<=R
"""
assert 0<=L
if L>R:
return 0
a=X.a; m=X.n
def calc(K):
""" sum_{i=0}^{K-1} a^i
"""
if K==0:
return 0
elif K%2==0:
return (1+pow(a, K//2, m))*calc(K//2)%m
else:
return (1+a*calc(K-1))%m
return Modulo(calc(R+1)-calc(L), m)
"""
有限体の操作関連
"""
#ルジャンドル記号
def Legendre(X: Modulo) -> int:
""" ルジャンドル記号 (a/p) を返す. ※ 法が素数のときのみ成立する.
Args:
X (Modulo):
Returns:
int:
X = 0 のときは 0
X が平方剰余のときは 1
X が平方非剰余のときは -1
"""
if 0 in X:
return 0
return 1 if pow(X, (X.n - 1) // 2) == 1 else -1
#根号
def Sqrt(X: Modulo) -> Modulo:
""" r * r = a (mod p) を満たす r を (存在すれば) 求める.
Args:
X (Modulo):
Returns:
Modulo: 存在しない場合は None, 存在する場合は r * r = a を満たす r (のうちの 1 つ)
"""
if Legendre(X) == -1:
return None
p = X.n
if X == 0:
return X
elif p == 2:
return X
elif p % 4 == 3:
return pow(X, (p + 1) // 4)
elif p % 8 == 5:
if pow(X, (p - 1) // 4) == 1:
return pow(X, (p + 3) // 8)
else:
return pow(2, (p - 1) // 4, p) * pow(X, (p + 3) // 8)
from random import randint as ri
u = 2
s = 1
while (p - 1) % (2 * u) == 0:
u *= 2
s += 1
q = (p - 1) // u
while True:
z = Modulo(ri(1, p - 1), p)
if pow(z, (p - 1) // 2) == -1:
break
m, c, t, r = s, pow(z, q), pow(X, q), pow(X, (q + 1) // 2)
while m > 1:
if pow(t, pow(2, m - 2)) == 1:
c = c * c
m = m - 1
else:
c, t, r, m = c * c, c * c * t, c * r, m - 1
return r
#離散対数
def Discrete_Log(A: Modulo, B: Modulo | int, default: int = -1) -> int | None:
""" A^x ≡ B を満たす最小の非負整数 x を求める.
Args:
A (Modulo): 底
B (Modulo | int): 真数
default (int, optional): 存在しないときの返り値. Defaults to -1.
Raises:
ValueError: A, B 共に Modulo のときは法を一致させなければならない.
Returns:
int | None: A^x ≡ B を満たす最小の非負整数 x (存在しない場合は default).
"""
A, M = A.a, A.n
if isinstance(B, int):
B %= M
elif isinstance(B, Modulo):
if M != B.n:
raise ValueError
B = B.a % M
else:
raise NotImplementedError
m = 0
while m * m < M:
m += 1
E = set()
y = B
for _ in range(m):
y *= A; y %= M
E.add(y)
step = pow(A, m, M)
head = 1 % M
count = 0
for k in range(1, m + 1):
tail = head
head = step * head % M
if head not in E:
continue
body = tail
for n in range((k - 1) * m, k * m):
if body == B:
return n
body = (A * body) % M
count += 1
if count == 2:
break
return default
def Order(X: Modulo, defalut: int = -1) -> int:
""" X^k = 1 を満たす最小の正の整数 k を求める (存在しない場合は -1)
Args:
X (Modulo): 底
defalut (int, optional): 存在しない場合の値. Defaults to -1.
Returns:
int: X^k を満たす最小の k
"""
phi=1
N=X.n
e=(N&(-N)).bit_length()-1
if e>0:
phi=1<<(e-1)
N>>=e
else:
phi=1
e=0
while N%3==0:
e+=1
N//=3
if e>0:
phi*=pow(3,e-1)*2
flag=0
p=5
while p*p<=N:
if N%p==0:
e=0
while N%p==0:
e+=1
N//=p
phi*=pow(p,e-1)*(p-1)
p+=2+2*flag
flag^=1
if N>1:
phi*=N-1
a=float("inf")
k=1
while k*k<=phi:
if phi%k==0:
if k<a and pow(X,k)==1:
a=k
break
if phi//k<a and pow(X,phi//k)==1:
a=phi//k
k+=1
return a if a < float("inf") else defalut
def Primitive_Root(p):
""" Z/pZ 上の原始根を見つける
p: 素数
"""
if p==2:
return 1
if p==998244353:
return 3
if p==10**9+7:
return 5
if p==163577857:
return 23
if p==167772161:
return 3
if p==469762049:
return 3
fac=[]
q=2
v=p-1
while v>=q*q:
e=0
while v%q==0:
e+=1
v//=q
if e>0:
fac.append(q)
q+=1
if v>1:
fac.append(v)
g=2
while g<p:
if pow(g,p-1,p)!=1:
return None
flag=True
for q in fac:
if pow(g,(p-1)//q,p)==1:
flag=False
break
if flag:
return g
g+=1
"""
数え上げ関連
"""
def Factor_Modulo(N, Mod, Mode=0):
"""
Mode=0: N! (mod Mod) を求める.
Mode=1: k! (mod Mod) (k=0,1,...,N) のリストも出力する.
[計算量]
O(N)
"""
if Mode==0:
X=1
for k in range(1,N+1):
X*=k; X%=Mod
return Modulo(X,Mod)
else:
L=[Modulo(1,Mod)]*(N+1)
for k in range(1,N+1):
L[k]=k*L[k-1]
return L
def Factor_Modulo_with_Inverse(N, Mod):
""" k=0,1,...,N に対する k! (mod Mod) と (k!)^(-1) (mod Mod) のリストを出力する.
[入力]
N, Mod: 整数
Mod >0
[出力]
長さ N+1 のリストのタプル (F,G): F[k]=k! (mod M), G[k]=(k!)^(-1) (mod M)
[計算量]
O(N+log Mod)
"""
assert Mod>0
F=Factor_Modulo(N,Mod,Mode=1)
G=[0]*(N+1)
G[-1]=F[-1].inverse()
for k in range(N,0,-1):
G[k-1]=k*G[k]
return F,G
def Binomial_Coefficient_Modulo(n: int, r: int, Mod:int):
""" nCr (mod Mod) を愚直な方法で求める.
[入力]
n, r, Mod: 整数
Mod>0
[出力]
nCr (mod Mod)
[計算量]
O(r)
"""
assert Mod>0
if r<0 or n<r:
return Modulo(0,Mod)
X=Y=1
r=min(r,n-r)
for i in range(r):
X*=n-i; X%=Mod
Y*=r-i; Y%=Mod
return Modulo(X,Mod)/Modulo(Y,Mod)
def Binomial_Coefficient_Modulo_List(n:int, Mod:int):
""" n を固定し, r=0,1,...,n としたときの nCr (mod Mod) のリストを出力する.
[入力]
n,Mod: 整数
Mod>0
[出力]
[nC0 (mod Mod), nC1 (mod Mod),..., nCn (mod Mod)]
[計算量]
O(n)
"""
assert Mod>0
L=[Modulo(1,Mod) for _ in range(n+1)]
I=Modulo_Inverse_List(Mod,n)
for r in range(1,n+1):
L[r]=(n+1-r)*I[r]*L[r-1]
return L
def Pascal_Triangle(N,M):
"""
0<=n<=N, 0<=r<=n の全てに対して nCr (mod M) のリストを出力する.
[入力]
N,M:整数
M>0
[出力] (mod M) を省略.
[[0C0], [1C0, 1C1], ... , [nC0, ... , nCn], ..., [NC0, ..., NCN]]
[計算量]
O(N^2)
"""
X=[Modulo(1,M)]
L=[[Modulo(1,M)]]
for n in range(N):
Y=[Modulo(1,M)]
for k in range(1,n+1):
Y.append(X[k]+X[k-1])
Y.append(Modulo(1,M))
X=Y
L.append(Y)
return L
Traceback (most recent call last):
File "/opt/hostedtoolcache/Python/3.13.3/x64/lib/python3.13/site-packages/onlinejudge_verify/documentation/build.py", line 71, in _render_source_code_stat
bundled_code = language.bundle(stat.path, basedir=basedir, options={'include_paths': [basedir]}).decode()
~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/hostedtoolcache/Python/3.13.3/x64/lib/python3.13/site-packages/onlinejudge_verify/languages/python.py", line 96, in bundle
raise NotImplementedError
NotImplementedError