题目链接:洛谷
题目描述:求整数$x\in [a,b]$使得$|2px \ mod \ 2q-q|$最小,如果有多个$x$输出最小的。
数据范围:$1\leq a,b,p,q\leq 10^9$
第一道类欧的不是模板的题??
首先一看就尝试一下二分,如何判断$2px \ mod \ 2q \in [l,r]$呢?我们发现
$$[2px \ mod \ 2q \in [l,r]]=\lfloor\frac{2px-l}{2q}\rfloor-\lfloor\frac{2px-r-1}{2q}\rfloor$$
所以存在$x\in [a,b]$使得$2px \ mod \ 2q \in [l,r]$,只需要判断$\sum_{x=a}^b(\lfloor\frac{2px-l}{2q}\rfloor-\lfloor\frac{2px-r-1}{2q}\rfloor)$是否大于0就可以了,这个使用类欧计算。
找到最小的偏差值$l$之后可以使用扩欧解同余方程。时间复杂度$O(\log^2 n)$。
1 #include<bits/stdc++.h> 2 #define Rint register int 3 using namespace std; 4 typedef long long LL; 5 LL t, a, b, p, q, len; 6 inline LL calc(LL a, LL b, LL c, LL n){ 7 if(!a || !n) return (n + 1) * (b / c); 8 if(n < 0) return 0; 9 LL m = (a * n + b) / c; 10 if(a >= c || b >= c) return calc(a % c, b % c, c, n) + (n * (n + 1) >> 1) * (a / c) + (n + 1) * (b / c); 11 return m * n - calc(c, c - b - 1, a, m - 1); 12 } 13 inline LL exgcd(LL a, LL b, LL &x, LL &y){ 14 if(!b){x = 1; y = 0; return a;} 15 LL gcd = exgcd(b, a % b, y, x); 16 y -= a / b * x; 17 return gcd; 18 } 19 int main(){ 20 scanf("%lld", &t); 21 while(t --){ 22 scanf("%lld%lld%lld%lld", &a, &b, &p, &q); 23 LL l = 0, r = q, mid, P = p << 1, Q = q << 1; 24 while(l < r){ 25 mid = l + r >> 1; 26 LL L = q - mid, R = q + mid + 1; 27 if(calc(P, Q - L, Q, b) + calc(P, Q - R, Q, a - 1) - calc(P, Q - L, Q, a - 1) - calc(P, Q - R, Q, b) > 0) r = mid; 28 else l = mid + 1; 29 } 30 LL x, y, gcd = exgcd(P, Q, x, y), ans = 1e9; P /= gcd; Q /= gcd; 31 if((q - l) % gcd == 0){ 32 LL xx = (q - l) / gcd * x; xx += (a - xx) / Q * Q; 33 while(xx >= a) xx -= Q; while(xx < a) xx += Q; 34 ans = min(ans, xx); 35 } 36 if((q + l) % gcd == 0){ 37 LL xx = (q + l) / gcd * x; xx += (a - xx) / Q * Q; 38 while(xx >= a) xx -= Q; while(xx < a) xx += Q; 39 ans = min(ans, xx); 40 } 41 printf("%lld\n", ans); 42 } 43 }CF1182F
后来看了一波官方题解,结果发现是一个类似BSGS的分块,时间复杂度$O(\sqrt{n})$。(写得不好还要带一个log...)
(还是上面那个方法更好一些)
(最主要是看起来更高大上一点。。。)