@51nod - 1132@ 覆盖数字的数量 V2
@description@
给出2个整数A,B。可以使用任意次。再给出一段从X - Y的区间T,任选若干个A,B做加法,可以覆盖区间T中多少个不同的整数。
例如:A = 8,B = 11,区间T为3 - 20。在3 - 20中,整数8(8),11(11),16(8+8),19(8+11)。可以被区间S中的数覆盖,因此输出4。
@solution@
首先变成 A,B 在 [0...r] 覆盖的数减去 A,B 在 [0...l-1] 覆盖的数。问题变成求 A,B 在 [0...N] 覆盖的数。
然后 A, B, N 同时除以 gcd(A, B),不过 N 可能不能整除,此时向下取整。
当 A, B 互质时,某数 X 一定可以表示为 \(p\times A + q\times B\),其中 \(0 \leq p < B\)。
因此我们枚举 p,得到 [0...N] 可被覆盖的数的数量:
\[\sum_{p=0}^{B-1且p\times A \leq N}(\lfloor\frac{N - p\times A}{B}\rfloor + 1)
\]
这是一个经典的类欧几里得。直接做即可。
@accepted code@
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
ll gcd(ll x, ll y) {return (y == 0 ? x : gcd(y, x % y));}
ll func(ll n, ll a, ll b, ll c) {
if( a >= c ) return func(n, a % c, b, c) + (a/c)*n*(n + 1)/2;
if( b >= c ) return func(n, a, b % c, c) + (b/c)*(n + 1);
ll m = (a * n + b) / c;
if( a == 0 || m == 0 ) return 0;
return n*m - func(m - 1, c, c - b - 1, a);
}
ll get(ll A, ll B, ll N) {
ll p = min(N / A, B - 1);
return func(p, A, N - p*A, B) + p;
}
void solve() {
ll A, B, X, Y; scanf("%lld%lld%lld%lld", &A, &B, &X, &Y), X--;
ll d = gcd(A, B); A /= d, B /= d, X /= d, Y /= d;
printf("%lld\n", get(A, B, Y) - get(A, B, X));
}
int main() {
int T; scanf("%d", &T);
while( T-- ) solve();
}
@details@
关于 A, B 互质时的覆盖 —— 想起「小凯的疑惑」。
基本上算是个类欧板子题吧。