题意
给定 M , D , L , R M,D,L,R M,D,L,R,求 L ≤ D x m o d M ≤ R L\leq Dx\bmod M\leq R L≤DxmodM≤R 的最小正整数解。
Solution
先考虑平凡的做法。
从 L L L 枚举到 R R R,每一个都跑一次扩展欧几里得,时间复杂度 O ( R − L + 1 log ( R − L + 1 ) ) O(R-L+1\log (R-L+1)) O(R−L+1log(R−L+1)),一看数据范围 1 0 9 10^9 109,可以考虑丢掉这种算法。
既然没办法用同余方程解决,那我们考虑将 m o d \bmod mod 变成减法的形式。
L ≤ D x m o d M ≤ R L\leq Dx\bmod M\leq R L≤DxmodM≤R
L ≤ D x − M y ≤ R L\leq Dx-My\leq R L≤Dx−My≤R
其中那个 y y y 是个整数。我们只要求出这个 y y y 就能求出 x x x。
现在移项,得到:
L − D x ≤ − M y ≤ R − D x L-Dx\leq -My\leq R-Dx L−Dx≤−My≤R−Dx
− R + D x ≤ M y ≤ − L + D x -R+Dx\leq My\leq -L+Dx −R+Dx≤My≤−L+Dx
求 y y y 的话我们就不能出现 x x x。尝试取模,同时 m o d D \bmod\ D mod D 可以消去 x x x,得到:
( − R ) m o d D ≤ M y m o d D ≤ ( − L ) m o d D (-R)\bmod D\leq My\bmod D\leq (-L)\bmod D (−R)modD≤MymodD≤(−L)modD
ohhhhhhhhh!这个形式跟我们的 L ≤ D x m o d M ≤ R L\leq Dx\bmod M\leq R L≤DxmodM≤R 是完全一致的!所以我们可以考虑使用递归求解。
设有函数 s o l v e ( d , m , l , r ) solve(d,m,l,r) solve(d,m,l,r) 求解的是 L ≤ D x m o d M ≤ R L\leq Dx\bmod M\leq R L≤DxmodM≤R 中 x x x 的最小正整数解,那么我们递归进去就应该是 s o l v e ( m m o d d , d , ( − r ) m o d d , ( − l ) m o d d ) solve(m\bmod d,d,(-r)\bmod d,(-l)\bmod d) solve(mmodd,d,(−r)modd,(−l)modd)。注意,因为取模的可乘性,所以 m m m 是可以 m o d d \bmod\ d mod d 的。
这个函数的复杂度显然是正确的,与辗转相除有异曲同工之妙。
写的时候注意向下取整的问题,不然你会像我这样一直差 1 1 1,急的像个猴子:link。
代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int T;
ll solve(ll d, ll m, ll l, ll r){
if(!d || l > r)
return -1;
ll x = (l - 1) / d + 1;
if(x * d <= r)
return (x % m + m) % m;
ll p = solve(m % d, d, ((-r) % d + d) % d, ((-l) % d + d) % d);
if(p == -1)
return -1;
return ((((l - 1) + m * p) / d + 1) % m + m) % m;
}
int main(){
scanf("%d", &T);
while(T--){
ll m, d, l, r;
scanf("%lld%lld%lld%lld", &m, &d, &l, &r);
r = min(m - 1, r);
printf("%lld\n", solve(d, m, l, r));
}
return 0;
}