题面
有 x \tt x x 个红糖, y \tt y y 个蓝糖。每一个礼包里面要么有 a \tt a a 个红糖+ b \tt b b 个蓝糖,要么是 a \tt a a 个蓝糖+ b \tt b b 个红糖。
问最多能打多少份礼包。
T ≤ 1 0 4 \tt T\leq 10^4 T≤104 组数据, 1 ≤ x , y , a , b ≤ 1 0 9 \tt1\leq x,y,a,b\leq10^9 1≤x,y,a,b≤109 。
题解
Solution #1
不难发现答案具有包含性,能打 n \tt n n 份就一定能打 n − 1 \tt n-1 n−1 份。
交换,令 a ≥ b \tt a\geq b a≥b,那么每打包一份礼包, x \tt x x 和 y \tt y y 都至少会减少 b \tt b b。
直接二分答案 s \tt s s,那么在 x , y ≥ s ⋅ b \tt x,y\geq s\cdot b x,y≥s⋅b 的前提下,再把 x \tt x x 和 y \tt y y 都减去 s ⋅ b \tt s\cdot b s⋅b 后,相当于在 x , y \tt x,y x,y 中只用找单独的 s \tt s s 个 a − b \tt a-b a−b 就行了( { x , y } \tt\{x,y\} {x,y} 变成了 { x − s b , y − s b } \tt\{x-sb,y-sb\} {x−sb,y−sb}, { a , b } \tt\{a,b\} {a,b} 变成了 { a − b , 0 } \tt\{a-b,0\} {a−b,0},其中一个为 0 了,两种糖果不再绑定),这等价于此时 a = b \tt a=b a=b 或者 ⌊ x a − b ⌋ + ⌊ y a − b ⌋ ≥ s \tt\left\lfloor\frac{x}{a-b}\right\rfloor+\left\lfloor\frac{y}{a-b}\right\rfloor\geq s ⌊a−bx⌋+⌊a−by⌋≥s 。
直到这里,都没怎么用脑子。 7 m i n \tt7~min 7 min 过掉,总复杂度 O ( T log ) \tt O(T\log) O(Tlog),速度还行, 31 m s \tt31~ms 31 ms。
CODE
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 100005
#define ENDL putchar('\n')
#define LL long long
#define DB double
#define lowbit(x) ((-x) & (x))
LL read() {
LL f = 1,x = 0;char s = getchar();
while(s < '0' || s > '9') {if(s=='-')f = -f;s = getchar();}
while(s >= '0' && s <= '9') {x=x*10+(s-'0');s = getchar();}
return f * x;
}
int n,m,i,j,s,o,k;
int X,Y,A,B;
bool check(int md) {
int x=X,y=Y,a=A,b=B;
if(x < md*1ll*B || y < md*1ll*B) return 0;
x -= md*1ll*B; y -= md*1ll*B; a -= b;
if(a == 0) return 1;
return x/a + y/a >= md;
}
int main() {
int T = read();
while(T --) {
X = read();Y = read();
A = read();B = read();
if(X < Y) swap(X,Y);
if(A < B) swap(A,B);
int as = 0;
for(int i = 30;i >= 0;i --) {
if(as+(1<<i) <= 1000000000 && check(as+(1<<i))) {
as += (1<<i);
}
}
printf("%d\n",as);
}
return 0;
}
Solution #2
不妨交换,令 x ≥ y , a ≥ b \tt x\geq y,a\geq b x≥y,a≥b 。
然后利用一定的贪心思路,不难发现,最优情况下 ( a , b ) \tt(a,b) (a,b) 的个数一定不小于 ( b , a ) \tt(b,a) (b,a)。换言之,我们可以认为有若干个权重为 1 的礼包 ( a , b ) \tt(a,b) (a,b),以及若干个权重为 2 的礼包 ( a + b , b + a ) \tt(a+b,b+a) (a+b,b+a).
先特判 a = b \tt a=b a=b 的情况,再继续讨论。
为了尽量地利用糖果,使之剩下的最少(每个礼包固定消耗 a + b \tt a+b a+b,因此剩下最少一定意味着礼包最多),那么就要尽量使最终的 ∣ x − y ∣ \tt|x-y| ∣x−y∣ 最小化。由于每一份礼包 1 都能减少差值 a − b \tt a-b a−b ,那么我们就令礼包 ( a , b ) \tt(a,b) (a,b) 的个数为 ⌊ x − y a − b ⌋ \tt\left\lfloor\frac{x-y}{a-b}\right\rfloor ⌊a−bx−y⌋ ,然后再利用剩下的求出礼包 2 的个数、贡献。
为了调整出正确答案,我们还得求一求礼包 1 个数为 ⌊ x − y a − b ⌋ + 1 \tt\left\lfloor\frac{x-y}{a-b}\right\rfloor+1 ⌊a−bx−y⌋+1 的情况,再取更优值。毕竟有些边角情况,原先的下取整是考虑不到的。
时间复杂度 O ( T ) \tt O(T) O(T) ,写得一般的还是 31 m s \tt31~ms 31 ms,而且想的比较久。
CODE(by SharpnessⅤ)
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define pre(i,a,b) for(int i=a;i>=b;i--)
#define N 500005
using namespace std;
int x,y,a,b;
void solve(){
scanf("%d%d%d%d",&x,&y,&a,&b);
if(x>y)swap(x,y);if(a>b)swap(a,b);
if(x<a||y<b){puts("0");return ;}
int res=y-x,lim=b-a;
if(!lim){
printf("%d\n",min(x,y)/a);
return ;
}
int cur=min(res/lim,min(x/a,y/b));
x-=cur*a,y-=cur*b;int ans=cur+min(x,y)/(a+b)*2;
if(x>=a&&y>=b)x-=a,y-=b,cur++;
printf("%d\n",max(ans,cur+min(x,y)/(a+b)*2));
}
int main(){
int T;scanf("%d",&T);
while(T--)solve();
return 0;
}