中国剩余定理 算法 (CRT) 信息学竞赛
前置技能
扩展欧几里得算法:
求出形似
a⋅x+b⋅y=1gcd(a,b)=1
也即
a⋅x+b⋅y=gcd(a,b)
的一组解 x,y
问题模型
对 x 满足以下模方程
求最小的
xmod(p1⋅p2⋅p3⋯pm)
解决方案
1> 我们先解决 m=2 的情况
{xmodp1xmodp2=a1=a2
求
xmod(p1⋅p2)
的值
我们设两个中间变量 r,s ,并将式子改变为
{x+r⋅p1x+s⋅p2=a1=a2
则
r⋅p1−s⋅p2=a1−a2
我们再设另外两个中间变量 r′,s′ 满足
{rs=r′⋅(a1−a2)=−s′⋅(a1−a2)
则上式变为
r′⋅p1+s′⋅p2=1
此时,我们可以就通过扩展欧几里得求得
r′,s′
了
回带即可得到一个
xmod(p1⋅p2)
的值了
2> 多个式子的合并
我们注意到两个式子合并为了一个方程
xmod(p1⋅p2)=b
这个方程的形式与其他方程的形式相同,并且模数互质,所以我们可以对这些方程两两合并
例题
/****************************************\
* Author : ztx
* Title : [cogs] 1786. 韩信点兵
* ALG : CRT
* CMT :
* Time :
\****************************************/
#include <cstdio>
#define Rep(i,l,r) for(i=(l);i<=(r);i++)
#define Rev(i,r,l) for(i=(r);i>=(l);i--)
#define rep(i,l,r) for(i=(l);i< (r);i++)
#define rev(i,r,l) for(i=(r);i> (l);i--)
typedef long long ll ;
typedef double lf ;
typedef long double llf ;
typedef unsigned uint ;
typedef unsigned long long ull ;
#define Getchar() getchar()
int CH , NEG ;
template <typename TP>inline void read(TP& ret) {
ret = NEG = 0 ; while (CH=Getchar() , CH<'!') ;
if (CH == '-') NEG = true , CH = Getchar() ;
while (ret = ret*10+CH-'0' , CH=Getchar() , CH>'!') ;
if (NEG) ret = -ret ;
}
template <typename TP>inline void readc(TP& ret) {
while (ret=Getchar() , ret<'!') ;
while (CH=Getchar() , CH>'!') ;
}
template <typename TP>inline void reads(TP *ret) {
ret[0]=0;while (CH=Getchar() , CH<'!') ;
while (ret[++ret[0]]=CH,CH=Getchar(),CH>'!') ;
ret[ret[0]+1] = 0 ;
}
#define maxm 11LL
#define abss(x) ((x)<0?-(x):(x))
ll tmp ;
inline void exgcd(ll a, ll b, ll&x, ll&y) {
if (b) exgcd(b,a%b,x,y) , tmp = x , x = y , y = tmp-a/b*y ;
else x = 1 , y = 0 ;
}
inline ll mul(ll a,ll b,ll module) {
ll ret = a*b-((ll)((llf)a*b/(llf)module+1E-3))*module ;
return (ret+module)%module ;
}
ll p[maxm] , a[maxm] ;
int main() {
int i , m ;
ll n , x , y , module ;
#define READ
#ifdef READ
freopen("HanXin.in" ,"r",stdin ) ;
freopen("HanXin.out","w",stdout) ;
#endif
read(n) , read(m) ;
read(p[1]) , read(a[1]) ;
module = p[1] ;
Rep (i,2,m) {
read(p[i]) , read(a[i]) ;
exgcd(module,p[i],x,y) ;
if (y < 0) y += module ;
module *= p[i] ;
y = mul(y,abss(a[i]-a[i-1]),module) ;
a[i] = (a[i]-mul(abss(y),p[i],module)) % module ;
}
ll ans = n-a[m]-(n-a[m])/module*module ;
if (ans > n) puts("-1") ;
else printf("%lld\n", ans) ;
#ifdef READ
fclose(stdin) ; fclose(stdout) ;
#else
Getchar() ; Getchar() ;
#endif
return 0 ;
}