网上都说这个题用中国剩余定理做,但是中国剩余定理的适用条件是:
求解x=a[i](mod n[i])方程组,且n[i]两两互质
但可以发现这个题中n[i]并不两两互质
想不出解法参考网上的代码发现好多人都是用暴力写的,偏偏说是中国剩余定理
我就对着用暴力写了一个,但是暴力的过程需要优化处理,否则会超时
另外从这道题中也学会了怎么求解多个数的最小公倍数
1、求解最小公倍数(lcm)方法:
之前做题求两个数的lcm时,都是用其乘积除以它们的gcd
于是这次就很自然的这么做,才发现这样做的方法错得很离谱
比如:求2 3 4的lcm 答案很明显是12
但是用上述方法求得出来的结果是1
所以正确的方法应该是每次求出当前结果与下一个数的lcm
如果想要一次求出所有数的lcm,正确的做法如下:
如求a[1-n]的lcm
令M=a[i]*...*a[n];
令b[i]=M/a[i]; b_gcd为b数组的gcd
则a数组的lcm为M/b_gcd
用第一种方式写出来的代码会更简洁些:
for(int i=0; i<num; ++i) {
scanf("%d", &a[i]);
lcm = lcm*a[i]/gcd(lcm, a[i]);
}
2、中国剩余定理
发现网上很多人说中国剩余定理的变形可以用来解决n[i]两两不互质的情况
因为感觉太繁琐了,又是模板型,就没有看,下面把普通的中国剩余定理代码贴上
#include <cmath>
#include <cstdio>
#include <iostream>
#include <algorithm>
#define MAXN 10010
#define LL long long
using namespace std;
int a[MAXN], n[MAXN], m[MAXN];
int gcd(int a, int b) {
return b==0 ? a : gcd(b, a%b);
}
int expand_gcd(int a, int b, int d, int &x, int &y) {//用于求ax+by=d的一个解
if(b == 0) {
d = a;
x = 1;
y = 0;
}
else {
expand_gcd(b, a%b, d, y, x);
y -= x*(a/b);
}
}
int inv(int a, int n) {//用于求逆元
int d, x, y;
expand_gcd(a, n, d, x, y);
return d==1 ? (x+n)%n : -1;
}
int chinese_remainder(int a[], int n[], int num) {
int M = 1;
int d, x, y;
for(int i=1; i<=num; ++i) {
M *= n[i];
}
int sum = 0;
for(int i=1; i<=num; ++i) {
m[i] = M/n[i];
expand_gcd(m[i], -n[i], d, x, y);
x = inv(m[i], -n[i]);
sum += (a[i]*x*m[i])%M;
}
return sum % M;
}
int main(void) {
int num;
while(scanf("%d", &num) != EOF) {
for(int i=1; i<=num; ++i) {
scanf("%d %d", &a[i], &n[i]);
}
cout << chinese_remainder(a, n, num) << endl;
}
return 0;
}
看到了一个很好的中国剩余定理对应n[i]非两两互质的说明
把图片传在这里
3、这道题的代码:
找到第一个i满足模方程组后,可知i+k*lcm均满足条件(k为非负数)
从这个形式可以发现第一个满足条件的一定是i 且i<=lcm
故在找i的for循环中可以限定条件i<=lcm,这样会大大优化程序运行时间
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAXN 10
#define LL long long
using namespace std;
int a[MAXN], b[MAXN];
int gcd(int a, int b) {
return b==0 ? a : gcd(b,a%b);
}
int main(void) {
int T;
int lcm;
bool flag;
scanf("%d", &T);
while(T--) {
int max, num;
scanf("%d%d", &max, &num);
lcm = 1;
flag = false;
for(int i=0; i<num; ++i) {
scanf("%d", &a[i]);
lcm = lcm*a[i]/gcd(lcm, a[i]);
}
for(int i=0; i<num; ++i) {
scanf("%d", &b[i]);
}
int i = 0, j;
for(i=1; i<=max && i<=lcm; ++i) {
for(j=0; j<num; ++j) {
if(i%a[j] != b[j]) {
break;
}
}
if(j == num) {
flag = true;
break;
}
}
if(!flag)
cout << "0" << endl;
else {
cout << (max-i)/lcm+1 << endl;
}
}
return 0;
}