[CF1538G] Gift Set (数学简单题)

题面

相信英文题面也很好理解

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 T104 组数据, 1 ≤ x , y , a , b ≤ 1 0 9 \tt1\leq x,y,a,b\leq10^9 1x,y,a,b109

题解

Solution #1

不难发现答案具有包含性,能打 n \tt n n 份就一定能打 n − 1 \tt n-1 n1 份。

交换,令 a ≥ b \tt a\geq b ab,那么每打包一份礼包, 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,ysb 的前提下,再把 x \tt x x y \tt y y 都减去 s ⋅ b \tt s\cdot b sb 后,相当于在 x , y \tt x,y x,y 中只用找单独的 s \tt s s a − b \tt a-b ab 就行了( { x , y } \tt\{x,y\} {x,y} 变成了 { x − s b , y − s b } \tt\{x-sb,y-sb\} {xsb,ysb} { a , b } \tt\{a,b\} {a,b} 变成了 { a − b , 0 } \tt\{a-b,0\} {ab,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 abx+abys

直到这里,都没怎么用脑子。 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 xy,ab

然后利用一定的贪心思路,不难发现,最优情况下 ( 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| xy 最小化。由于每一份礼包 1 都能减少差值 a − b \tt a-b ab ,那么我们就令礼包 ( a , b ) \tt(a,b) (a,b) 的个数为 ⌊ x − y a − b ⌋ \tt\left\lfloor\frac{x-y}{a-b}\right\rfloor abxy ,然后再利用剩下的求出礼包 2 的个数、贡献。

为了调整出正确答案,我们还得求一求礼包 1 个数为 ⌊ x − y a − b ⌋ + 1 \tt\left\lfloor\frac{x-y}{a-b}\right\rfloor+1 abxy+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;
}
posted @ 2021-06-16 21:59  DD_XYX  阅读(37)  评论(0编辑  收藏  举报