洛谷 P3868 [TJOI2009]猜数字
现有两组数字,每组k个,第一组中的数字分别为:a1,a2,...,ak表示,第二组中的数字分别用b1,b2,...,bk表示。其中第二组中的数字是两两互素的。求最小的非负整数n,满足对于任意的i,n - ai能被bi整除。
中国剩余定理的裸题
题目要求的是最小的非负整数\(n\)满足\(b_{i}|n-a_{i}(1\le i\le k)\)
将其转化成同余问题的方程组
\(\begin{cases}n&\equiv &a_{1}(mod\ b_{1})\\n&\equiv &a_{2}(mod\ b_{2})\\n&\equiv &a_{3}(mod\ b_{3})\\…&…&\\n&\equiv &a_{k}(mod\ b_{k})\end{cases}\)
那么如何去求呢,这就要用到中国剩余定理了
我们设\(M=\prod_{i=1}^{n}b_{i}\),\(m_{i}=M/b_{i}\)
\(x_{i}\)是方程\(m_{i}\times x_{i}\equiv 1(mod\ b_{i})\)的一个解,可以由扩展欧几里得算法求得
那么答案\(n\)就为\(\sum_{i=1}^{n}a_{i}\times m_{i}\times x_{i}\)
为什么呢,我们来证明一下
因为\(m_{i}=M/b_{i}\)是除了\(b_{i}\)之外的所有\(b\)的积
所以\(\forall k\ne i,a_{i}\times m_{i}\times x_{i}\equiv 0(mod\ m_{k})\)
由\(t_{i}\)是方程\(m_{i}\times x_{i}\equiv 1(mod\ b_{i})\),的一个解得\(a_{i}\times m_{i}\times x_{i}\equiv a_{i}(mod\ b_{i})\)
代入\(n=\sum_{i=1}^{n}a_{i}\times m_{i}\times x_{i}\),原方程组成立。
而我们要求的是最小非负整数解,那么只需要将\(n\)取模\(M\)使其落在0~M-1的范围内即可。
这道题的坑点在于直接相乘的话会爆\(long\ long\),那么就需要用到快速乘了,思想跟快速幂一样,写起来也很简单
还有一点就是对于负数的情况要加上\(M\)再模\(M\)
Code
#include <iostream>
#include <cstdio>
#define int long long
using namespace std;
int k,a[20],b[20],M=1,ans;
void exgcd(int a,int b,int &x,int &y) //扩展欧几里得算法求线性同余方程的解
{
if (!b)x=1,y=0;
else
{
exgcd(b,a%b,x,y);
int t=x;
x=y;
y=t-a/b*y;
}
}
int cc(int x,int y) //快速乘
{
int s=0;
x=(x%M+M)%M;
y=(y%M+M)%M; //先将负数转化为正数
while (y)
{
if (y&1)s=(s+x)%M;
x=x*2%M;
y>>=1;
}
return s;
}
signed main()
{
cin>>k;
for (int i=1;i<=k;i++)
cin>>a[i];
for (int i=1;i<=k;i++)
cin>>b[i],M*=b[i];
for (int i=1;i<=k;i++)
{
int m=M/b[i],x=0,y=0;
exgcd(m,b[i],x,y);
ans=(ans+cc(cc(a[i],m),x))%M;
}
cout<<ans<<endl;
return 0;
}