题意是,你想给两个朋友送礼物,礼物表示成正整数,第一个朋友不喜欢能被x整除的礼物,第二个朋友不喜欢能被y整除的礼物,这两个朋友分别要送cnt1和cnt2个礼物,假设礼物从1到v编号,让你求出最小的v使得能送满足条件的礼物给两个朋友,两个朋友的礼物不能一样。其中上述变量满足
1 ≤ cnt1, cnt2 < 109
;
cnt1 + cnt2 ≤ 109
;
2 ≤ x < y ≤ 3·104
v最少是cnt1+cnt2,最大应该不超过cnt1+cnt2的3倍,也就是当x为2,y为3是,v应该取到最大值。
有了下界和上界,就可以用二分来找到最小的v值了,那么就判断一个数mid能不能以题目的要求分发礼物。
设礼物为1到mid,那么这mid个礼物就可一分成几类,分别是:能分发给第一个朋友但不能分发给第二个朋友的,能分发给第二个朋友但不能分发给第一个朋友的,既可以分发给第一个朋友也可以分发给第二个朋友的,既不可以分发给第一个朋友也不可以分发给第二个朋友的。其中第四种的个数最好求,就是mid / (x * y),第一种就是可以被y整除但不能被x整除的数的个数,为mid / y - mid / (x * y),第二种是可以被x整除但不能被y整除的数的个数,为mid / x - mid / (x * y),第三种为总数减去剩下三个的和,记为c。
我们将第一种的个数分给第一个朋友,第二种的个数分给第二个朋友,设分过之后他们还分别差c1和c2个礼物,这就要使用第三种礼物了,可以很快得到,如果c1,c2和c1+c2这三个数都不大于第三种的个数,那么mid就是符合题意的。这样我们就可以很快的判断mid能否满足题意,二分就可以了。
我写的时候忘记了带括号,一直错,最后才发现了,坑惨了。
代码如下:
#include <cstdio>
#include <iostream>
using namespace std;
long long cnt1, cnt2, x, y;
int main(void)
{
while (cin >> cnt1 >> cnt2 >> x >> y) {
long long l = cnt1 + cnt2;
long long r = 3 * l;
long long ans = cnt1 + cnt2;
while (l <= r) {
long long mid = (l + r) / 2;
long long tmp1 = mid / x;
long long tmp2 = mid / y;
long long tmp3 = mid - tmp1 - tmp2 + mid / x / y;
//真心坑,这里写错了,应该带上括号的,忘记带了,带上就过了
long long tmp4 = cnt1 - (tmp2 - mid / x / y);
long long tmp5 = cnt2 - (tmp1 - mid / x / y);
if (tmp4 <= tmp3 && tmp5 <= tmp3 && tmp4 + tmp5 <= tmp3) {
ans = mid;
r = mid - 1;
} else {
l = mid + 1;
}
}
cout << ans << endl;
}
return 0;
}