洛谷P6767 [BalticOI 2020/2012 Day0] Roses 题解

发布时间 2023-09-23 22:24:22作者: xuantianhao

题解 P6767 Roses

题目传送门


\(a,c\) 为每束花的朵数,\(b,d\) 为每束花需要的钱

首先简单了解一下题意,大概就是现在给你 \(n\) 朵花,每 \(a\) 朵花 \(b\) 元,每 \(c\) 朵花 \(d\) 元,求最少需要多少钱?

注意:

这里 \(n\) 的范围是 \(\le 10^{15}\) 并且保证数据答案 \(\le 10^{18}\)

所以要开\(\texttt{long long}\)!!!

(犇蒟蒻第一次写的时候就没有注意到,然后喜提 \(\texttt{TLE}\) 。。。)

然后就和你自己花钱去买东西一样,比如你手里有十块钱,这一家超市五块钱可以买两瓶冰红茶,那一家超市10块钱可以买三瓶冰红茶,经过你聪明的大脑计算,就会发现,傻子都知道要去第一家超市

上面这个事情运用到的其实就是性价比,而这道题目也是一样的,用 \(\texttt{double}\) 计算出两种花的性价比
(我看其他题解都用到了 \(\texttt{long double}\) ,其实不需要,因为 \(a,b,c,d\) 的范围都是 \(\le 10^{5}\) ,直接用 \(\texttt{double}\) 就可以了)

以下是计算性价比的代码:

double a1=1.0*b/a;
double a2=1.0*d/c;
//由于a,b,c,d都是正整数,所以在运算前要先乘上一个1.0使其转成浮点数

然后我们可以不妨设a朵b元的方案是性价比高的,实际中假如第二种方案高那就把他们反一下,具体操作如下:

double a1=1.0*/a;
double a2=1.0*d/c;
//由于a,b,c,d都是正整数,所以在运算前要先乘上一个1.0使其转成浮点数
if(a1>a2){
   swap(a,c);
   swap(b,d);
}

接着我们可以用 \(\texttt{num}\) 存只买第一种方案的数量,但是很明显只买第一种是不对的,所以我们还需要枚举第二种方案

但是关键就是该怎么枚举呢?假如买k组第二种,我们就需要枚举 \(\frac{n}{c}\) 次,显然时间 100% 爆炸,后来经过我的草稿纸一顿嘎嘎乱算,发现,假如
\(k*c \equiv 0 \pmod a\)

就可以只买 \(\frac{k*c}{a}\) 组第一种方案

这样子就只要枚举到 \(\frac{a}{gcd(a,c)}\) 就OK了

这里还要插一点 \(\operatorname{gcd}\) 函数,犇蒟蒻的 \(\operatorname{gcd}\) 写的和网上的不太一样,但是跑起来挺快的,这里给大家展示一下(勿喷):

inline int gcd(int a,int b){
	if(b) while((a%=b)&&(b%=a));
	return a+b;
}

最后贴上犇蒟蒻的代码
码风很丑,还请见谅

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL n,a,b,c,d;
inline LL read(){//快读
    LL x=0,f=1;char ch=getchar();
    while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
    return x*f;
}
inline int gcd(int a,int b){//最大公约数函数
	if(b) while((a%=b)&&(b%=a));
	return a+b;
}
LL ans;
int main(){
	//freopen("roses.in","r",stdin);
	//freopen("roses.out","w",stdout);
	n=read();
	a=read();b=read();c=read();d=read();
	double a1=1.0*b/a;
   double a2=1.0*d/c;
	if(a1>a2){
      swap(a,c);
      swap(b,d);
   }
	LL num1=(n%a==0)?n/a:(n/a+1);
	ans=num1*b;
	LL num=a/gcd(a,c);
	for(LL i=num;i>=0;i--){
		LL now=i*d;
		if(i*c<=n){
			LL k=n-i*c;
			LL num2=(k%a==0)?k/a:(k/a+1);
			now+=num2*b;
		}
		ans=min(ans,now);
	}
	printf("%lld",ans);
	return 0;
} 
//10001 144 287 125 249
//19929
//一组小数据,供参考