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

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

posted @ 2020-03-09 12:46  Tiw_Air_OAO  阅读(168)  评论(0编辑  收藏  举报