X问题
X问题
求在小于等于N的正整数中有多少个X满足:
X mod a[0] = b[0],
X mod a[1] = b[1],
X mod a[2] = b[2],
…,
X mod a[i] = b[i],
…
(0 < a[i] <= 10)。
思路
跟[[中国剩余定理#模板 | 中国剩余定理]]一样的结构, 那么就转化一下:
x = k1*a[0] + b[0]
x = k2 * a[1] + b[1]
先看前两个式子如何合并:
k1*a[0] + b[0] = k2 * a[1] + b[1]
移项得:
k1 * a[0] - k2 * a[1] = b[1] - b[0]
因为正负无所谓, 所以这里直接把 a[1]
变成负数
k1 * a[0] + k2 * a[1] = b[1] - b[0]
通过扩展欧几里得定理得出 k1, k2 的值, 然后化为通解
k1 = k1 + k*(a[1] / d)
k2 = k2 + k*(a[0] / d)
带入x中得:
x = (k1 + k * a[1] / d)*a[0] + b[0]
x = k * (a[1] / d) * a[0] + k1*a[0] + b[0]
故合并后x的式子中:
a 为 k * (a[1] / d) * a[0]
b 为 k1 * a[0] +b[0]
则更新时这样更新:
b1 = k1 * a1 + b1;
a1 = abs((a1 / d) * a[i]); // a1 一般比 a[i] 大, 故防溢出先除 a1
最后的答案 x = b1 + k*a1
其中 b1 就是最小的x, 求在N之下有多少个x成立, 则将 k 变成1, 2, 3 ... 直到 x 超出n即可。
根据扩展欧几里得定理可得, 当 b[1] - b[0]
无法被 gcd(a1, a[i])
整除时, 无法合并, 无解。
代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <cmath>
using namespace std;
inline int readInt()
{
int t;
scanf("%d", &t);
return t;
}
template <typename T>
inline T exgcd(T a, T b, T &x, T &y)
{
if (!b)
{
x = 1, y = 0;
return a;
}
T t = exgcd(b, a % b, y, x);
y -= a / b * x;
return t;
}
//------- Coding Area ---------//
const int N = 1e2 + 10, MOD = 100003;
// int f[N][N];
long long n, m;
long long a[12], b[12];
int main()
{
int T;
cin >> T;
while (T--)
{
cin >> n >> m;
for(int i = 0; i < m; i++) a[i] = readInt();
for(int i = 0; i < m; i++) b[i] = readInt();
long long a1 = a[0], b1 = b[0], k1, k2;
bool flag = true;
for(int i = 1; i < m; i++)
{
long long d = exgcd(a1, -a[i], k1, k2);
if ((b[i] - b1) % d)
flag = false;
// 这里是优化为最小整数解, 提高速度
k1 *= (b[i] - b1) / d;
long long t = a[i] / d;
k1 = (k1 % t + t) % t;
b1 = k1 * a1 + b1;
a1 = abs((a1 / d) * a[i]);
}
if (flag && b1 <= n)
{
b1 = (b1 % a1 + a1) % a1;
long long res = (n - b1) / a1; // 这是除去 最小非负整数x 之后的其他解
if (b1 != 0) // 若 x 不为0则算一个解
res++;
cout << res << endl;
}
else
cout << 0 << endl;
}
return 0;
}