CJOJ 1659 【中学高级本】倒酒

【中学高级本】倒酒

Description

Winy是一家酒吧的老板,他的酒吧提供两种体积的啤酒,a ml和b ml,分别使用容积为a ml和b ml的酒杯来装载。
酒吧的生意并不好。Winy发现酒鬼们都非常穷。有时,他们会因为负担不起aml或者bml啤酒的消费,而不得不离去。因此,Winy决定出售第三种体积的啤酒(较小体积的啤酒)。
Winy只有两种杯子,容积分别为a ml和b ml,而且啤酒杯是没有刻度的。他只能通过两种杯子和酒桶间的互相倾倒来得到新的体积的酒。
为了简化倒酒的步骤,Winy规定:
(1)a≥b;
(2)酒桶容积无限大,酒桶中酒的体积也是无限大(但远小于桶的容积);
(3)只包含三种可能的倒酒操作:
①将酒桶中的酒倒入容积为b ml的酒杯中;
②将容积为a ml的酒杯中的酒倒入酒桶;
③将容积为b ml的酒杯中的酒倒入容积为a ml的酒杯中。
(4)每次倒酒必须把杯子倒满或把被倾倒的杯子倒空。
Winy希望通过若干次倾倒得到容积为a ml酒杯中剩下的酒的体积尽可能小,他请求你帮助他设计倾倒的方案

Input

两个整数a和b(0<b≤a≤10^9)

Output

第一行一个整数c,表示可以得到的酒的最小体积。
第二行两个整数Pa和Pb(中间用一个空格分隔),分别表示从体积为a ml的酒杯中倒出酒的次数和将酒倒入体积为b ml的酒杯中的次数。
若有多种可能的Pa、Pb满足要求,那么请输出Pa最小的一个。若在Pa最小的情况下,有多个Pb满足要求,请输出Pb最小的一个。

Sample Input

5 3

Sample Output

1
1 2

Hint

样例提示:
倾倒的方案为:
1、桶->B杯; 2、B杯->A杯;
3、桶->B杯; 4、B杯->A杯;
5、A杯->桶; 6、B杯->A杯;

Source

数学,扩展欧几里德

Solution

相当于求方程by - ax = gcd(a, b)的最小正整数解

Code

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define L
#define LL long long
using namespace std;

inline int gi() {
  char cj = getchar();
  int ans = 0, f = 1;
  while (cj < '0' || cj > '9') {
    if (cj == '-') f = -1;cj = getchar();
  }
  while (cj >= '0' && cj <= '9') ans = ans * 10 + cj - '0', cj = getchar();
  return f * ans;
}

LL a, b, x, y;

inline LL gcd(LL a, LL b) {
  return (a % b == 0) ? b : gcd(b, a % b);
}

inline void exgcd(LL a, LL b, LL& x, LL& y) {
  if (b == 0) {x = 1, y = 0;}
  else {
    exgcd(b, a % b, y, x);
    y -= (a / b) * x;
  }
}

int main() {
  freopen("CJOJ1659.in", "r", stdin);
  freopen("CJOJ1659.out", "w", stdout);
  a = gi(), b = gi();
  LL temp = gcd(a, b);
  a /= temp, b /= temp;
  exgcd(a, b, x, y);
  x *= -1;
  while (x > 0 || y > 0) x -= b, y -= a;
  while (x < 0 || y < 0) x += b, y += a;
  printf("%lld\n%lld %lld\n", temp, x, y);
  return 0;
}

Summary

扩展解系的时候,不需要将gcd乘进去,因为扩展欧几里得求出了(b / gcd)y - (a / gcd)x = (gcd / gcd);就相当于等式两边同时除以gcd,所以最终答案不用乘以gcd

你可能感兴趣的:(数论)