能用中国剩余定理(Chinese Remainder Theorem, CRT)合并互质同余,用扩展 CRT(exCRT)处理非互质模数,判断无解并控制乘法溢出。
前置:gcd、扩展欧几里得、模逆、线性同余。
已合并为
$$ x\equiv r\pmod M, $$
再加入 $x\equiv b\pmod m$。令 $x=r+Mt$,得到
$$ Mt\equiv b-r\pmod m. $$
设 $g=\gcd(M,m)$。若 $g\nmid(b-r)$ 无解;否则约去 $g$,M/g 在模 m/g 下可逆,求出最小非负 t。新余数为 r+M*t,新模数为 lcm(M,m),再规范化余数。
这就是 exCRT:逐条合并,不要求模数两两互质。
M*t 可能超过 64 位;题目未保证安全时用 __int128 做中间乘法。互质 CRT 给出模 21252 的唯一解类。合并余数 r 后,答案是 (r-d) mod 21252,若结果为 0 则取 21252,因为题目要求未来而非当天。CRT 唯一性保证该正代表是下一次而非某次更晚相遇。
r, period = CRT([(p,23), (e,28), (i,33)])
days = (r - d) mod period
if days == 0: days = period
x ≡ remainder (mod modulus),模数不保证互质,求最小非负解或 -1。(r,M) 完整代表此前所有方程,不需保存旧式子。初始 (0,1) 表示所有整数。逐条代入 x=r+Mt 解线性同余;若兼容则得到所有共同解的唯一剩余类,更新为规范化的 (r,lcm)。归纳可知处理 k 条后恰表示前 k 条共同解集;最终 r 是最小非负解。每次 $O(\log\max modulus)$。
r, M = 0, 1
for (modulus m, remainder b):
g, inverse = extended_gcd(M, m)
if (b-r) is not divisible by g: return -1
t = ((b-r)/g * inverse) mod (m/g)
r = r + M*t
M = lcm(M,m); r %= M
(S+Kx) mod N = 0,无解输出 -1。线性同余有解当且仅当 g 整除 S。约分后 K/g 与 N/g 互质,其逆元存在;乘到右侧并规范化就得到模 N/g 的唯一解类,最小非负代表即答案。每组 $O(\log N)$。
g = gcd(K,N)
if S % g != 0: answer = -1
else:
K,N,S = K/g,N/g,S/g
answer = (-S * inverse(K mod N)) mod N
三盏信号灯同时从绿灯开始。第 $i$ 盏以 $g_i$ 秒绿、$r_i$ 秒红循环,且所有 $g_i,r_i$ 都在 $[1,10^{12}]$。求接下来
$$ (g_1+r_1)(g_2+r_2)(g_3+r_3) $$
秒内三灯同时为绿的总时长,模 $998244353$。
整商 + 一个短前缀 精确保留了这些重数。先令 $p_i=g_i+r_i$。三个原余数 $(x_1,x_2,x_3)$ 能由同一时刻产生,当且仅当
$$ x_i\equiv x_j\pmod{\gcd(p_i,p_j)} $$
对每对 $i,j$ 都成立。对第 $i$ 个余数,所有会参与这些条件的信息恰合并为模 $q_i$ 的余数。于是每个压缩后兼容三元组由唯一的 $t\bmod L$ 表示,其绿色原像数为三个权重之积,得到上面的求和式。
设 base_i=g_i//q_i、rem_i=g_i%q_i,则
$$ w_i(t)=base_i+[t\bmod q_i<rem_i]. $$
选 $q_i$ 最小的一盏作为 0 号灯。对另外两盏,权重只在每个周期起点和 起点+rem 改变;用两个“下一断点”指针合并扫描。相邻断点 $[l,r)$ 内另外两个权重恒定,而 0 号灯可用前缀公式求和:
$$ F_i(T)=base_iT+\left\lfloor\frac{T}{q_i}\right\rfloor rem_i +\min(T\bmod q_i,rem_i), $$
所以区间权重和是 $F_i(r)-F_i(l)$。
最后还需恢复题目要求的长区间。每个兼容原余数组在一个 $\operatorname{lcm}(p_1,p_2,p_3)$ 周期中出现一次,而题目区间长度是 $p_1p_2p_3$,故乘上
$$ \frac{p_1p_2p_3}{\operatorname{lcm}(p_1,p_2,p_3)}. $$
实现中可写为
$$ \gcd(p_1,p_2)\cdot\gcd(\operatorname{lcm}(p_1,p_2),p_3), $$
并用 __int128 计算中间 lcm,避免乘法溢出。
rem_i 个余数再贡献一次,故 $w_i$ 正确。三个灯的原像独立选择,乘积就是该压缩三元组对应的绿色原三元组数。把 $q_i$ 写成 $gab,gac,gbc$ 并选择最小者后,扫描事件数为 $O(a+b)$,其中较大的 $a,b$ 仍不超过 $\sqrt{2\times10^{12}}$ 量级;其余运算为常数次 gcd/lcm。空间 $O(1)$(实现直接生成下一断点,不保存事件表)。
p[i] = g[i] + r[i]
q[i] = lcm(gcd(p[i],p[j]), gcd(p[i],p[k]))
L = lcm(q[0],q[1],q[2])
reorder lights so q[0] is minimum
answer_one_period = 0
left = 0
while left < L:
right = min(next change of w[1], next change of w[2], L)
answer_one_period += w[1](left) * w[2](left)
* (prefix_w[0](right) - prefix_w[0](left))
left = right
repeat = p[0]*p[1]*p[2] / lcm(p[0],p[1],p[2])
output answer_one_period * repeat modulo 998244353
完整 GNU C++17 参考实现:AtCoder-agc050_e.cpp
__int128。rem_i=0 时该权重恒定,不应生成“周期起点与结束点重合”的重复事件。CRT 解决的是多个同一未知量的同余交集;高斯消元解决多个未知量的线性系统。模数不是质数时,多变量系统可考虑质因数分解后分别求解再 CRT,但素数幂上的消元仍需处理非单位主元。
枚举小模数所有 x 对拍 exCRT,覆盖兼容非互质、不兼容、某模数整除另一模数。用接近 $10^{18}$ 的数检查中间乘法是否需要 128 位。