中国剩余定理【CRT】
题目
解析
题意:给定
a
1
…
k
a_{1…k}
a1…k及
b
1
…
k
b_{1…k}
b1…k,求满足
{
(
n
−
a
1
)
∣
b
1
(
n
−
a
2
)
∣
b
2
.
.
.
(
n
−
a
k
)
∣
b
k
\left\{\begin{aligned}(n-a_1)\mid b_1 \\ (n-a_2)\mid b_2\\ ...\\ (n-a_k)\mid b_k\end{aligned}\right.
⎩⎪⎪⎪⎪⎨⎪⎪⎪⎪⎧(n−a1)∣b1(n−a2)∣b2...(n−ak)∣bk
的最小正整数n
显然的CRTChinese Re/Tle裸题,这里讲一下什么是CRT
中国剩余定理用于求解一个最小非负整数,使其满足
{
n
≡
a
1
(
m
o
d
m
1
)
n
≡
a
2
(
m
o
d
m
2
)
.
.
.
n
≡
a
k
(
m
o
d
m
k
)
\left\{\begin{aligned} n\equiv a_1(\mod m_1)\\ n\equiv a_2(\mod m_2)\\...\\ n\equiv a_k(\mod m_k)\\ \end{aligned}\right.
⎩⎪⎪⎪⎪⎨⎪⎪⎪⎪⎧n≡a1(modm1)n≡a2(modm2)...n≡ak(modmk)
其中m两两互质
设
M
=
∏
i
=
1
k
m
i
M
i
=
M
m
i
T
i
=
M
i
−
1
(
m
o
d
m
i
)
M=\prod_{i=1}^{k}m_i\qquad M_i=\frac{M}{m_i}\qquad T_i=M_i^{-1}(\mod m_i)
M=∏i=1kmiMi=miMTi=Mi−1(modmi)
即
M
i
T
i
≡
1
(
m
o
d
m
i
)
M_iT_i\equiv1(\mod m_i)
MiTi≡1(modmi),有特解
a
n
s
0
=
∑
i
=
1
k
a
i
M
i
T
i
ans_0=\sum_{i=1}^{k}a_iM_iT_i
ans0=∑i=1kaiMiTi,任意解
a
n
s
=
a
n
s
0
+
k
M
ans=ans_0+kM
ans=ans0+kM
注意到
m
i
m_i
mi不一定为质数,我们需要用exgcd求逆元
证明:
∀
j
∈
[
1
,
k
]
,
i
≠
j
∀j∈[1,k],i≠j
∀j∈[1,k],i=j,因
m
i
∣
M
j
m_i\mid M_j
mi∣Mj,故有
a
j
M
j
T
j
≡
0
(
m
o
d
m
i
)
a_jM_jT_j\equiv0(\mod m_i)
ajMjTj≡0(modmi)
又因
M
i
T
i
≡
1
(
m
o
d
m
i
)
M_iT_i\equiv1(\mod m_i)
MiTi≡1(modmi),故有
a
i
M
i
T
i
≡
a
i
(
m
o
d
m
i
)
a_iM_iT_i\equiv a_i(\mod m_i)
aiMiTi≡ai(modmi)
故得证
逆元:
注意到 m i m_i mi不一定是质数,需要使用exgcd或欧拉定理求逆元
乘法:
P3868需要龟速乘
code(P3868):
#include<cstdio>
#define int long long
using namespace std;
inline void exgcd(int a,int b,int &x,int &y)
{
if(b==0){x=1,y=0;return;}
exgcd(b,a%b,x,y);
int tmp=x;
x=y,y=tmp-a/b*y;
}
int x,y;
inline int _(int a,int b)
{
exgcd(a,b,x,y);
return (y%a+a)%a;
}
int n,a[11],b[11],M=1,ans;
inline int mul(int xx,int yy)
{
int res=0;
while(yy){if(yy&1)res=((res+xx>=M)?(res+xx-M):(res+xx));yy>>=1,xx=((xx<<1)>=M)?((xx<<1)-M):(xx<<1);}
return res;
}
signed main()
{
scanf("%lld",&n);
for(int i=1;i<=n;++i)scanf("%lld",&a[i]);
for(int i=1;i<=n;++i)scanf("%lld",&b[i]),M=M*b[i];
for(int i=1;i<=n;++i)ans=(ans+mul(_(b[i],M/b[i]),mul(M/b[i],(a[i]+M)%M)))%M;
printf("%lld",(ans<0)?(ans+M):ans);
return 0;
}
code(P1495&&ybtoj):
#include<cstdio>
#define ll __int128
using namespace std;
inline bool idigit(char x){return (x<'0'|x>'9')?0:1;}
inline ll read()
{
ll num=0,f=1;
char c=0;
while(!idigit(c=getchar())){if(c=='-')f=-1;}
while(idigit(c))num=(num<<1)+(num<<3)+(c&15),c=getchar();
return num*f;
}
inline void write(ll x)
{
ll F[20];
ll tmp=x>0?x:-x;
if(x<0)putchar('-');
ll cnt=0;
while(tmp>0){F[cnt++]=tmp%10+'0';tmp/=10;}
while(cnt>0)putchar(F[--cnt]);
if(x==0)putchar('0');
}
inline void exgcd(ll a,ll b,ll &x,ll &y)
{
if(b==0){x=1,y=0;return;}
exgcd(b,a%b,x,y);
ll tmp=x;
x=y,y=tmp-a/b*y;
}
ll x,y;
inline ll _(ll a,ll b)
{
exgcd(a,b,x,y);
return (x%b+b)%b;
}
ll n,a[11],b[11],M=1,ans;
int main()
{
n=read();
for(ll i=1;i<=n;++i)a[i]=read(),b[i]=read(),M*=a[i];
for(ll i=1;i<=n;++i)ans+=b[i]*(M/a[i])*_(M/a[i],a[i]);
write(ans%M);
return 0;
}