$2020.11.21$ 模拟赛(持续更新)
T1 Jams 倒酒(pour)
题目大意:给出两个容积分别为a和b的酒杯与一个装有无限多酒的酒桶。规定以下三种操作:用酒桶中的酒将容积为b的酒杯填满、将容积为a的酒杯中的酒全部倒入酒桶中、将容积为b的酒杯中的酒倒入容积为a的酒杯中。其中在进行第三种操作时,若容积为a的酒杯在倒了一部分酒后就达到了容积,那么剩余的酒将留在容积为b的酒杯中。求无限次操作后,能在容积为a的酒杯中倒出的最小体积的酒以及第二种操作和第一种操作(在倒出的酒的体积不变的情况下)的最小操作数。
我们经过观察,发现题目可以转化成:对于不定方程\(bx - ay = d\),求出最小的\(d、y、x\)。为了便于用\(exgcd\)计算,我们将不定方程\(bx - ay = d\)转换为\(bx + ay = d\),要求求出的值也变成了\(d、-y、x\)。
————————————————————————————————————————————————————————————
定理\(1\):对于不定方程\(bx + ay = d\),最小的\(d\)为\(\gcd(a, b)\)。
证:设存在\(d_0 < \gcd(a, b)\),则有\(bx_0 + ay_0 = d_0\)。
将不定方程\(bx_0 + ay_0 = d_0\)两边同时除以\(\gcd(a, b)\),得到\(\dfrac{bx_0}{\gcd(a, b)} + \dfrac{ay_0}{\gcd(a, b)} = \dfrac{d_0}{\gcd(a, b)}\)。
因为\(a \mid \gcd(a,b), \space b \mid \gcd(a, b)\),所以\((\dfrac{bx_0}{\gcd(a, b)} + \dfrac{ay_0}{\gcd(a, b)}) \mid 1 ,\dfrac{d_0}{\gcd(a, b)} \mid 1\)。
因为\(d_0 < \gcd(a, b)\),所以\(\dfrac{d_0}{\gcd(a, b)} \nmid 1\),与上述证明冲突。
证毕。
————————————————————————————————————————————————————————————
随后我们使用\(exgcd\)求出不定方程\(bx + ay = \gcd(a, b)\)的一组特解\(x_0、y_0\)。由于操作数必须为正整数,所以需要将\(y_0\)变为最小正整数。设新的一组特解为\((x_1, y_1)\),则\(y_1= y_0 + \lfloor \dfrac{y_0\gcd(a, b)}{a} \rfloor * y_0\)。将\(y_1\)代入不定方程\(bx_1 + ay_1 = \gcd(a, b)\),得出\(x_1\)的值。输出\(d、-y、x\)即可。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
#define int long long
int a, b, x, y, k0, k1, Gcd;
int exgcd(int a0, int b0)
{
if (b0 == 0)
{
x = 1;
y = 0;
return a0;
}
int d = exgcd(b0, a0 % b0);
int z = x;
x = y;
y = z - y * (a0 / b0);
return d;
}
signed main()
{
freopen("pour.in", "r", stdin);
freopen("pour.out", "w", stdout);
cin >> a >> b;
if (a == b)
{
cout << a << endl << "0 1";
return 0;
}
Gcd = exgcd(a, b);
cout << Gcd << '\n';
k0 = b / Gcd;
k1 = a / Gcd;
y += (y / k1) * k1;
if (y < 0) y += k1;
cout << -(Gcd - y * b) / a << " " << y;
return 0;
}
T2 土豪聪要请客
题目大意:给出一张由'.'或'X'组成的大厅地图,规定只有由'.'组成的长方形区域才可以放下桌子。一个由'.'组成的长为\(m\),宽为\(n\)的长方形所能容纳的桌子可以坐下\(2(n+m) - 1\)个人,求在大厅中最多能坐下的人数。