D 韩信点兵

性质: 给定整数 a 和 b , 对 a 加上 b 的任意倍数, a % b 的结果不变
=> 如果 x 满足约束 x % a = b , 则 x + ka 也满足约束
=> 方程 x % a = b, 特解 x = x0 的通解为 x = x0 + ka

令 x[i] 为满足前i个约束的解的最小值
X[i] 为满足前i个约束的解的通解
Lcm[i]=lcm(a[0],a[1],,...,a[i])

只考虑前i个约束的通解:
X[i] = x[i] + k1 * Lcm[i]

考虑第i+1个约束:
X[i+1] = x[i+1] + k2 * Lcm[i+1] = k3 * Lcm[i]

因为 X[i+1] ⊆ X[i] (满足前i+1个约束,一定满足前i个约束)

则可得到递推式:
① x[i+1] = x[i] + t*Lcm[i], 其中t从0开始枚举, 直到满足x[i+1] % a[i] == b[i]
② Lcm[i+1] = lcm( Lcm[i], a[i+1]) 

初值:
lcm初值为1
x初始值为0, (x=1 WA了1个, 别问我怎么知道的, 我调两天了)

如何判断无解:

现在有新的约束 (a[i], b[i]), 要用 x[i-1] 和 lcm[i-1] 求 x[i]

即找到一个(最小的)t, 使得 x[i] = x[i-1] + t * lcm[i-1]

(x + t * lcm) % a = b 
⇔ t * lcm = b - x (mod a)

这是一个同余方程, t有解的充要条件是 gcd(a,lcm)|(b-x)

最后: 解出 x 后, 如果大于m则输出"可能说谎", 否则输出x
import java.math.BigInteger;
import java.util.Scanner;

public class Main {
    static Scanner sc = new Scanner(System.in);

    static int I() {
        return sc.nextInt();
    }

    static long L() {
        return sc.nextLong();
    }

    public static void main(String[] args) {
        int n = I();
        BigInteger m = BigInteger.valueOf(L());
        //x[i]:满足前i个约束的最小x; lcm[i]:前i个约束的最小公倍数
        BigInteger x = BigInteger.ZERO, lcm = BigInteger.ONE;
        for (int i = 0; i < n; i++) {
            BigInteger a = BigInteger.valueOf(L()), b = BigInteger.valueOf(L());// 约束 ans % a = b
            // (x+t*lcm)%a=b => lcm * t = b-x (mod a)
            // 形如同余方程 ax = b ( mod p )
            // ax = b (mod p) 有解条件 gcd(a,p)|b
            // 即 (b-x) % gcd(lcm,a) = 0
            BigInteger gcd = lcm.gcd(a);
            if (!b.subtract(x).mod(gcd).equals(BigInteger.ZERO)) {// 无解
                System.out.println("he was definitely lying");
                return;
            }
            while (!x.mod(a).equals(b)) {
                x = x.add(lcm); // x[i+1] = x[i] + t*Lcm[i]
            }
            lcm = lcm.multiply(a).divide(gcd); // Lcm[i+1] = lcm( Lcm[i], a[i+1]) 
        }
        System.out.println(x.compareTo(m) > 0 ? "he was probably lying" : x);
    }
}