@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 互质时的覆盖 —— 想起「小凯的疑惑」。

基本上算是个类欧板子题吧。

上一篇:51nod 2989 组合数


下一篇:51nod 1001 数组中和等于K的数对(STL/双指针)