NKOI 5月月赛 观光车(拓展欧几里德)

 观光车
                                                          时间限制:10000 MS   空间限制:65536 KB
问题描述

何老板带领n名游客来到一景区大门口,需要乘坐观光车游览景区。

景区提供两种观光车,一种是每辆车可以坐a名游客,包一辆车费用是p1块钱;另一种每辆车可以坐b名游客,包一辆车费用是p2块钱。

何老板想让这n名游客都坐上观光车,且每辆车都坐满。问何老板至少要花费多少钱?

输入格式

第一行,一个整数n,表示游客的总数。

第二行,两个空格间隔的整数,表示p1和a

第三行,两个空格间隔的整数,表示p2和b

输出格式

一行,一个整数,表示所需最少费用。

如果无解,输出“-1”

样例输入1:
43
1 3
2 4
样例输出1:
15
样例输入2:
40
5 9
5 12
样例输出2:
-1
数据范围:1 <= n,a,b,p1,p2 <= 2,000,000,000

我们设x为一种车的辆数,y为另一种的辆数,则可以列出方程:ax+by=n
因此想到用拓展欧几里德,且满足x,y>=0
分析:
1.拓展欧几里德求出ax+by=gcd(a,b)=d的解x0,y0,若n%d!=0,则无解
2.ax+by=n通解为x=x0*n/d+b*k/d ,  y=y0*n/d-a*k/d,k为整数3.
3.因为x>=0,有 -x0*n/d<=b*k/d ==> -x0*n/b<=k
   同理y>=0,有 y0*n/d>=a*k/d  ==> y0*n/a>=k
   于是有   -x0*n/b <=k<= y0*n/a
   显然 如果-x0*n/b>y0*n/a 则无解 
4.总费用ans=p1*(x0*n/d+b*k/d)+p2*(y0*n/d-a*k/d )
                  =((p1*b-p2*a)/d)*k+(p1*x0*n+p2*y0*n)/d
                  =((p1*b-p2*a)*k+(p1*x0*n+p2*y0*n))/d
5.我们只需讨论关于k的函数(p1*b-p2*a)*k+(p1*x0*n+p2*y0*n)
   当(p1*b-p2*a)>0时,显然k要取最小才能使ans最小
   当(p1*b-p2*a)<0时,显然k要取最大才能使ans最小
   而k的范围已在前面算出,是-x0*n/b <=k<= y0*n/a
   我们只需讨论k的两个极值-x0*n/b和y0*n/a 然后算出答案即可
#include<iostream>
#include<cstdio>
#include<cmath>
#define er long long
using namespace std;
er n,p1,a,p2,b;
er adv(er a,er b,er &r,er &t){
  er k,tmp; 
if(b==0){r=1;t=0;return a;} 
k=adv(b,a%b,r,t); 
  tmp=r;r=t;t=tmp-a/b*t;
    return k; 
}
int main(){
cin>>n>>p1>>a>>p2>>b;
er x,y;
er gys=adv(a,b,x,y);
er t1=ceil(-1.0*n*x/b),t2=floor(1.0*n*y/a),t;//t1,t2分别为k的两个范围
if(n%gys!=0||t1>t2){puts("-1");return 0;}//无解的情况
//判断一次函数单调性
if(b*p1>a*p2)t=t1;
else t=t2;
er ans1=n*x/gys+b*t/gys,ans2=n*y/gys-a*t/gys;//ans1就是x的解,ans2就是y的解
cout<<ans1*p1+ans2*p2;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值