중국 잉여 정리의 CRT
중국 잉여 정리 동일한 선형 방정식을 해결하는데 사용된다.
\ [\ 시작 정렬 {} \ 좌측 \ {\ {시작 행렬 X} \ 당량 한 c_1 (MOD \ \, m_1과 \\) X \ 당량 C_2 (MOD \ \ m_2) \\ \\ X ... \ 당량 c_n (MOD \ \
, m_n) \ {단부 매트릭스} \ 오른쪽. \ 단부 정렬 {} \] 중국 잉여 정리오고있다.
의는 다음과 같은 식을 생각해 보자 :
. \ [\를} {시작 정렬 \ 왼쪽 \ {\}는 시작 {매트릭스 \ 당량 1 (MOD \, \, m_1과) \\ X_1 \ 당량 0 (MOD \, \, M_2을 X_1 \\) ... \\ X_1 \ 당량 0 (MOD \ \ m_n) \ {단부 매트릭스} \ 오른쪽. \ 좌측 \ {\ {시작 매트릭스 X_2} \ 당량 0 (MOD \ \, m_1과) \\ X_2 \ 1 당량 (MOD \ \, m_2) \\ \\ X_2 ... \ 당량 0 (MOD \ \ m_n) \ {단부 매트릭스} \ 오른쪽. \ 좌측 \ {는 \ {시작 행렬 x_n} \ 당량 0 (MOD \ \, m_1과 \\) x_n \ 당량 0 (MOD \ \ m_2) \\ ... \\ x_n \ 1 당량 (MOD \ \ m_n) \ {단부 행렬 .} \ 오른쪽 \ 단부 정렬 {
} \] 다음으로,에 \ (GCD (M1, M2, ..., m_n) \) 가 분명 할 때 다음과 같은 결론이 :
경우
\는 [\ 시작 \ {정렬} prod_ {I \ NEQ 1} m_i X_1 '& \ 당량 1 (MOD \ \, m_1과 \\) \ prod_ {I \ NEQ 2} m_i X_2'& \ 당량 1 (MOD을 \ \ m_2) \\ \
prod_ {내가 \ NEQ 않음} m_i x_n '{정렬} \ 당량 1 (MOD \ \ m_n) \\ \ 단부 \] 그래서
\ [\ 시작 {정렬} X_1 & = X_1은 '\ prod_ {I \는 1 NEQ} m_i \\ X_2 & = X_2'\ prod_ {난 NEQ 2 \} m_i \\ x_n & = x_n '\ prod_는 m_i \\ \ 단부 {난 NEQ 않음을 \} {정렬 } \]
그 최종 결과는
\ [X = \ sum_ {I
= 1} ^ nc_ix_i + K \ prod_ I = {1} ^ nm_i \] 다음 루틴을 작성하는 것이 좋다.
#include <bits/stdc++.h>
#define LL long long
using namespace std;
LL n;
LL A[ 20 ], B[ 20 ];
LL M, Ans, T[ 20 ];
LL QM( LL x, LL y ) {
LL Ans = 0;
for( ; y; y >>= 1, x = x * 2 % M )
if( y & 1 ) Ans = ( Ans + x ) % M;
return Ans;
}
void Expower( LL a, LL b, LL &x, LL &y ) {
if( b == 0 ) {
x = 1; y = 0; return;
}
Expower( b, a % b, y, x );
y -= a / b * x;
return;
}
LL INV( LL a, LL b ) {
LL x, y;
Expower( a, b, x, y );
if( x < 0 ) x += b;
return x;
}
int main() {
scanf( "%lld", &n );
for( LL i = 1; i <= n; ++i ) scanf( "%lld", &A[ i ] );
for( LL i = 1; i <= n; ++i ) scanf( "%lld", &B[ i ] );
for( LL i = 1; i <= n; ++i ) A[ i ] %= B[ i ];
M = 1;
for( LL i = 1; i <= n; ++i ) M *= B[ i ];
for( LL i = 1; i <= n; ++i ) T[ i ] = QM( INV( M / B[ i ], B[ i ] ), ( M / B[ i ] ) );
Ans = 0;
for( LL i = 1; i <= n; ++i ) Ans = ( Ans + QM( A[ i ], T[ i ] ) ) % M;
printf( "%lld\n", Ans );
return 0;
}
확장, 중국 잉여 정리의 ExCRT
다만, 중국 잉여 정리는 상대적으로 프라임 타임을 모듈로 적용 언급. 계수가 상대적으로 소수가 아닌 경우, 우리는 확장, 중국 잉여 정리를 사용해야합니다.
ExCRT은 다음과 같이 작동합니다 :
먼저 우리는 2 개의 선형 합동 식을 준수
\ [\ 상기 시작 {X} \의 당량의 한 c_1과 (MOD \를 \ m_1과) X 및 \\ \ 당량 C_2 (MOD \ \ M_2) 배향 최종 \ {정렬 } \]
이 양식에 작성한다 :
\ [\ 배향 선두 {X} + 및 k_1m_1 = 한 c_1과 C_2 + = \\ 및 k_2m_2 배향 최종 \ {} \] X
동시 구한 후 :
\ [\ 배향 선두 { } 한 c_1 + k_1m_1 & = C_2 +
k_2m_2 \\ \ RIGHTARROW k_1m_1-k_2m_2 & = C_2 - 한 c_1 \ 단부 {정렬} \] 슈 페이 정리로부터 제조에 충분한 조건, 방정식 해결의 가능성이있다 \ (GCD (m_1과, m_2) | (C_2-한 c_1) \) .
이 경우, 우리는 얻을 수 있습니다 :
\ [\를 k_1 \ FRAC {m_1과} {GCD (m_1과, M_2)} {(가) 정렬} 시작 - K_2 \ FRAC {M_2} {GCD (m_1과, M_2)} = \ FRAC {c_2- 한 c_1} {GCD (m_1과, m_2 )} \\ \ RIGHTARROW 및 k_1 \ FRAC {m_1과} {GCD (m_1과, m_2)} \ 당량 \ FRAC {C_2 - 한 c_1} {GCD (m_1과, m_2)} \ \ ( 개조 \ \ \ FRAC { m_2} {GCD (m_1과, m_2)}) \\ \ RIGHTARROW 및 k_1 \ 당량 \ FRAC {C_2 - 한 c_1} {GCD (m_1과, m_2)} \ 시간 (\ FRAC {m_1과} {GCD (m_1과, m_2)}
) ^ {- 1} \ \ (MOD \ \ \ {FRAC m_2} {GCD (m_1과, m_2)}) \ {단부 정렬} \] 다음 \ (k_1 \) 위로 치환 \ (X = k_1m_1 \) 한 c_1 + 수득
\ [X \ 당량 \ {FRAC C_2 - 한 c_1} {GCD (m_1과, m_2)} \ 시간 (\ FRAC m_1과 {} {GCD (m_1과, m_2 )}) ^ {- 1}
\ 시간 m_1과 + C_2 \ \ (MOD \는 \ \ FRAC는 {m_2}가 {GCD는 (m_1과는 m_2는)}) \] 우리는 같은 형상을 가지고 \ (X의 \ 당량의 C \ \ (MOD \ \, m) \) 선형 합동 식. 그래서 반복적 인 솔루션이 될 수 있습니다.
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int Maxm = 20;
void Work();
int main() {
int TestCases;
scanf( "%d", &TestCases );
for( ; TestCases; --TestCases ) Work();
return 0;
}
int N, M, A[ Maxm ], B[ Maxm ];
struct equation {
LL A, B;
};
equation T1, T2;
LL GCD( LL x, LL y ) {
LL m = x % y;
while( m ) {
x = y; y = m; m = x % y;
}
return y;
}
void ExGCD( LL a, LL b, LL &x, LL &y ) {
if( b == 0 ) {
x = 1; y = 0; return;
}
ExGCD( b, a % b, y, x );
y -= a / b * x;
return;
}
LL Inv( LL a, LL b ) {
LL x, y;
ExGCD( a, b, x, y );
if( x < 0 ) x += b;
return x;
}
equation ExCRT( equation X, equation Y ) {
LL Gcd = GCD( X.A, Y.A );
if( ( Y.B - X.B ) % Gcd ) return ( equation ) { 0, 0 };
LL A = X.A * Y.A / Gcd;
LL B = Inv( X.A / Gcd, Y.A / Gcd ) * ( Y.B - X.B ) / Gcd % ( Y.A / Gcd ) * X.A + X.B;
return ( equation ) { A, B };
}
void Work() {
scanf( "%d%d", &N, &M );
for( int i = 1; i <= M; ++i ) scanf( "%d", &A[ i ] );
for( int i = 1; i <= M; ++i ) scanf( "%d", &B[ i ] );
T1 = ( equation ) { A[ 1 ], B[ 1 ] };
for( int i = 2; i <= M; ++i ) {
T2 = ( equation ) { A[ i ], B[ i ] };
T1 = ExCRT( T1, T2 );
if( !T1.A ) {
printf( "0\n" );
return;
}
}
if( T1.B < 0 ) T1.B += T1.A;
if( T1.B > N ) {
printf( "0\n" );
return;
}
if( T1.B ) printf( "%d\n", ( int ) ( ( N - T1.B ) / T1.A + 1 ) );
else printf( "%d\n", ( int ) ( N / T1.A ) );
return;
}