洛谷 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;
}
posted @ 2020-06-08 21:25  eee_hoho  阅读(39)  评论(0编辑  收藏  举报