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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值