1. 程式人生 > >Luogu P3951 小凱的疑惑

Luogu P3951 小凱的疑惑

題目描述

小凱手中有兩種面值的金幣,兩種面值均為正整數且彼此互素。每種金幣小凱都有 無數個。在不找零的情況下,僅憑這兩種金幣,有些物品他是無法準確支付的。現在小 凱想知道在無法準確支付的物品中,最貴的價值是多少金幣?注意:輸入資料保證存在 小凱無法準確支付的商品。

輸入輸出格式

輸入格式:

 

兩個正整數 aa 和 bb,它們之間用一個空格隔開,表示小凱中金幣的面值。

 

輸出格式:

 

一個正整數 NN,表示不找零的情況下,小凱用手中的金幣不能準確支付的最貴的物品的價值。

 

輸入輸出樣例

輸入樣例#1: 複製

3 7

輸出樣例#1: 複製

11

說明

【輸入輸出樣例 1 說明】

小凱手中有面值為33和77的金幣無數個,在不找零的前提下無法準確支付價值為1, 2,4,5,8,111,2,4,5,8,11 的物品,其中最貴的物品價值為 1111,比1111 貴的物品都能買到,比如:

12 = 3 \times 4 + 7 \times 012=3×4+7×0

13 = 3 \times 2 + 7 \times 113=3×2+7×1

14 = 3 \times 0 + 7 \times 214=3×0+7×2

15 = 3 \times 5 + 7 \times 015=3×5+7×0

【資料範圍與約定】

對於 30\%30%的資料: 1 \le a,b \le 501≤a,b≤50。

對於 60\%60%的資料: 1 \le a,b \le 10^41≤a,b≤104。

對於100\%100%的資料:1 \le a,b \le 10^91≤a,b≤109。

 

feel:打表找規律

假設兩種錢的面額為aa和bb,且\gcd(a,b)=1gcd(a,b)=1。

假設兩種錢每種最少要拿一次(也就是不能不拿),那麼不能湊成的最大錢數k=a\times bk=a×b(注意是最大錢數,我剛才打錯了),由於可以不拿,那麼就把多拿的兩張錢減去,也就是ans=k-a-b=a\times b-a-bans=k−a−b=a×b−a−b,其實這裡大家都應該比較透徹,但是這個為什麼k=a\times bk=a×b我在下面證明一下。

現在我們需要證明ax+by=k(x,y>0)ax+by=k(x,y>0)無解。

我們利用反證法,設k=a\times bk=a×b,假設ax+by=k(x,y>0)ax+by=k(x,y>0)有解。

那麼根據歐幾里得演算法,ax+by=sax+by=s(ss是任意整數)有解的條件是\gcd(a,b)|sgcd(a,b)∣s。而這裡\gcd(a,b)=1gcd(a,b)=1,滿足條件。

我們假設找到了ax+by=1ax+by=1的一個解為(x_0,y_0)(x0​,y0​),那麼就有ax_0+by_0=1ax0​+by0​=1。因為a,b\ge1a,b≥1,直覺告訴我們x_0\le0x0​≤0或者y_0\le0y0​≤0,這個不用我證明了吧。

等式兩邊同時乘以kk,得到akx_0+bky_0=kakx0​+bky0​=k,即k=ax+byk=ax+by的一個解為(kx_0,ky_0)(kx0​,ky0​),根據通解公式,通解為\begin{aligned}\left(kx_0+\frac {bt} {\gcd(a,b)},ky_0-\frac {at} {\gcd(a,b)}\right)\end{aligned}(t\in \rm Z)(kx0​+gcd(a,b)bt​,ky0​−gcd(a,b)at​)​(t∈Z)。因為gcd(a,b)=1gcd(a,b)=1,所以通解為(kx_0+bt,ky_0-at)(kx0​+bt,ky0​−at)。因為k=a\times b>0k=a×b>0,而x_0\le 0x0​≤0或y_0\le0y0​≤0,所以x\le0x≤0或y\le0y≤0。當我們改變tt的值時,把通解轉化一下(b(ax_0+t),a(by_0-t))(b(ax0​+t),a(by0​−t))觀察這個式子,我們會發現其中一定有一個會\le0≤0的。我們繼續轉化,令X=ax_0+tX=ax0​+t,令Y=by_0-tY=by0​−t,則X+Y=1X+Y=1。因為他們都是整數,所以X\le0X≤0或Y\le0Y≤0的。而a,b>0,x=aX,y=bYa,b>0,x=aX,y=bY,所以x\le0x≤0或y\le0y≤0,即ax+by=k(x,y>0)ax+by=k(x,y>0)無解。

證畢。


為了嚴謹,我們還需要證明ax+by=k+r(x,y>0,rax+by=k+r(x,y>0,r是正整數))有解。和上面思路差不多,為了嚴謹,我重新寫一遍思路吧。

現在我們需要證明ax+by=k+r(x,y>0,rax+by=k+r(x,y>0,r是正整數))有解。

那麼根據歐幾里得演算法,ax+by=sax+by=s(ss是任意整數)有解的條件是\gcd(a,b)|sgcd(a,b)∣s。而這裡\gcd(a,b)=1gcd(a,b)=1,滿足條件。

我們假設找到了ax+by=1ax+by=1的一個解為(x_0,y_0)(x0​,y0​),那麼就有ax_0+by_0=1ax0​+by0​=1。因為a,b\ge1a,b≥1,直覺告訴我們x_0\le0x0​≤0或者y_0\le0y0​≤0,這個不用我證明了吧。

等式兩邊同時乘以k+rk+r,得到a(k+r)x_0+b(k+r)y_0=k+ra(k+r)x0​+b(k+r)y0​=k+r,即k+r=ax+byk+r=ax+by的一個解為(kx_0+rx_0,ky_0+ry_0)(kx0​+rx0​,ky0​+ry0​),根據通解公式,通解為\begin{aligned}\left(kx_0+rx_0+\frac {bt} {\gcd(a,b)},ky_0+ry_0-\frac {at} {\gcd(a,b)}\right)\end{aligned}(t\in \rm Z)(kx0​+rx0​+gcd(a,b)bt​,ky0​+ry0​−gcd(a,b)at​)​(t∈Z)。因為gcd(a,b)=1gcd(a,b)=1,所以通解為(kx_0+rx_0+bt,ky_0+ry_0-at)(kx0​+rx0​+bt,ky0​+ry0​−at)即(abx_0+rx_0+bt,aby_0+ry_0-at)(abx0​+rx0​+bt,aby0​+ry0​−at)。現在我們需要證明他們倆都大於0有解。

我們還是轉化一下,通解轉化為\begin{aligned}\left({b\left(ax_0+\frac{rx_0}b+t\right),a\left(by_0+\frac{ry_0}a-t\right)}\right)\end{aligned}(b(ax0​+brx0​​+t),a(by0​+ary0​​−t))​,注意這裡的除法是實數除法。那麼現在需要證明\begin{aligned}ax_0+\frac{rx_0}b+t\end{aligned}ax0​+brx0​​+t​和\begin{aligned}by_0+\frac{ry_0}a-t\end{aligned}by0​+ary0​​−t​都大於0。令X=\begin{aligned}ax_0+\frac{rx_0}b+t\end{aligned}X=ax0​+brx0​​+t​,Y=\begin{aligned}by_0+\frac{ry_0}a-t\end{aligned}Y=by0​+ary0​​−t​,則\begin{aligned}X+Y=ax_0+by_0+\frac{rx_0}b+\frac{ry_0}a=1+r\frac{ax_0+by_0}{ab}=1+\frac r {ab}>1\end{aligned}X+Y=ax0​+by0​+brx0​​+ary0​​=1+rabax0​+by0​​=1+abr​>1​。注意到這個式子中XX和YY一定可以構造出XX和YY都大於0的解。

為什麼呢?這個其實很好想,你可以把\begin{aligned}ax_0+\frac{rx_0}{b}\end{aligned}ax0​+brx0​​​和\begin{aligned}by_0+\frac{ry_0}{a}\end{aligned}by0​+ary0​​​想象成任意兩個和大於1的實數,你每次可以做的操作是把他們其中一個加1另一個-1,那麼他們一定可以同時為正。如何證明?我們建立一個座標系uOvuOv(實在沒字母可用了),然後建立引數方程\begin{aligned}u=ax_0+\frac{rx_0}{b}+t\end{aligned}u=ax0​+brx0​​+t​,\begin{aligned}v=by_0+\frac{ry_0}{a}-t\end{aligned}v=by0​+ary0​​−t​,其中tt是引數,t\in\rm Zt∈Z,我們先假設t\in\rm Rt∈R,那麼方程就是一條斜率為-1−1的直線,因為u+v>1u+v>1,所以直線在第一象限的長度大於\sqrt22​。因為t\in\rm Zt∈Z,t\in\rm Zt∈Z時候的影象就是在t\in\rm Rt∈R情況上的一系列間距為\sqrt 22​的點,而t\in\rm Rt∈R時候的影象在第一象限內長度大於\sqrt22​,所以t\in\rm Zt∈Z時候的影象在第一象限記憶體在點。

所以x>0x>0並且y>0y>0的解一定存在。

證畢。(上面就是我抄的Luogu上的題解,多麼誠實的我啊

#include<cstdio>
#define ll long long
using namespace std;
int main()
{
	ll a,b;
	scanf("%lld%lld",&a,&b);
	printf("%lld",a*b-a-b);
}