中国剩余定理也称孙子定理,是中国古代求解一次同余式组(见同余)的方法。是数论中一个重要定理。
这玩意在luogu居然有模板题:
[TJOI2009]猜数字
先来看一个问题:
在《孙子算经》中有这样一个问题:“今有物不知其数,三三数之剩二(除以3余2),五五数之剩三(除以5余3),七七数之剩二(除以7余2),问物几何?”
熟悉吗小学数学学过吗
这样的问题是基本的中国剩余定理的运用,解题过程有三步:
- 找出三个数:从(3)和(5)的公倍数中找出被(7)除余(1)的最小数(15),从(3)和(7)的公倍数中找出被(5)除余(1)的最小数(21),最后从(5)和(7)的公倍数中找出除(3)余(1)的最小数(70)。
- 用(15)乘以(2)((2)为最终结果除以(7)的余数),用(21)乘以(3)((3)为最终结果除以(5)的余数),同理,用(70)乘以(2)((2)为最终结果除以(3)的余数),然后把三个乘积相加(15∗2+21∗3+70∗2)得到和(233)。
- 用(233)除以(3,5,7)三个数的最小公倍数(105),得到余数(23),即(233%105=23)。这个余数23就是符合条件的最小数。
不得不佩服古人的智慧
然后看回题目(链接在开头)
[egin{cases}
(n-a_1)|b_1\
(n-a_2)|b_2\
...\
(n-a_k)|b_k
end{cases}\
]
变形成同余方程组之后:
[egin{cases}
(n-a_1≡0)pmod {b_1}\
(n-a_2≡0)pmod {b_2}\
...\
(n-a_k≡0)pmod {b_k}\
end{cases}
]
很明显的,
如果(a≡bpmod{m}),则(a+c≡b+cpmod{m})成立。
这个不难理解,稍微理解一下就可以了。
然后把上面同余方程组变形一下:
[ egin{cases}\
nequiv a_1(mod b_1)quad \ nequiv a_2(mod b_2)quad \ ...quad \
nequiv a_k(mod b_k)quad \ end{cases}\
]
然后到这里就是中国剩余定理的裸题了。但是需要注意的是因为题目问题,我们需要对每一个(a_i)作这样的操作:
(
a[i]=(a[i]mod b[i]+b[i])modb[i];
)
然后还需要注意的就是,这道题目因为数据良(bian)心(tai),所以还需要用快速乘来防止爆long long的情况。
代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll a[110],b[110];
ll k;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
inline ll qmul(ll a, ll b, ll m)
{
ll res=0;
while(b>0)
{
if(b&1)res=(res+a)%m;
a=(a+a)%m;
b>>=1;
}
return res;
}
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);
int tmp=x;
x=y,y=tmp-a/b*y;
return;
}
ll China()
{
ll ans=0,lcm=1;
for(ll i=1;i<=k;i++)
lcm*=b[i];
ll x,y;
for(ll i=1;i<=k;i++)
{
ll p=lcm/b[i];
exgcd(p,b[i],x,y);
x=(x%b[i]+b[i])%b[i];
ans=(ans+qmul(qmul(p,x,lcm),a[i],lcm))%lcm;
}
return (ans+lcm)%lcm;
}
int main()
{
cin>>k;
for(int i=1;i<=k;i++)
a[i]=read();
for(int i=1;i<=k;i++)
b[i]=read(),a[i]=(a[i]%b[i]+b[i])%b[i];
cout<<China();
return 0;
}
ov.