E. MORE SWEET
描述
ZZUACM集训队有n位队员,编号1~n由左至右,站成一排。
教练从左向右发苹果,每a个队员获得一个苹果(a号是第一个得到苹果的),从右往左发香蕉,每b个队员获得一个香蕉,(n-b+1)号是第一个得到香蕉的)。
问题:教练想要知道有多少队员既获得了苹果又获得了香蕉。
输入
输入三个正整数n,a,b。
数据规范:
*1≤n≤1000000000
*1≤a,b≤1000007
输出
输出一个整数,表示有多少队员既获得了苹果又获得了香蕉。
样例
in
8 3 2
out
1
首先大多数算法都由暴力转化而来,所以我们先思考最简单的暴力做法,如下:
暴力超时
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int main() {
LL n,a,b,x,num=0;
cin>>n>>a>>b;
x=n%b+1;
for(int i=1;i<=n;i++){
if(i%a==0&&(i-x)%b==0)num++;
}
if(x%a==0)num++;
cout<<num<<endl;
}
1 简单写法
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
signed main()
{
int n, a, b; cin >> n >> a >> b;
int i = 1;
while((n + 1 - i * a) % b != 0) i ++;
int d = a * b / __gcd(a, b);
cout << (n - i * a) / d + 1 << endl;
}
2 扩展欧几里得写法
然后进入重头戏:欧几里得算法
百度百科介绍:扩展欧几里得算法是欧几里得算法(又叫辗转相除法)的扩展。除了计算a、b两个整数的最大公约数,此算法还能找到整数x、y(其中一个很可能是负数)。通常谈到最大公因子时, 我们都会提到一个非常基本的事实: 给予二整数 a 与 b, 必存在有整数 x 与 y 使得ax + by = gcd(a,b)。有两个数a,b,对它们进行辗转相除法,可得它们的最大公约数——这是众所周知的。然后,收集辗转相除法中产生的式子,倒回去,可以得到ax+by=gcd(a,b)的整数解。
其实就一句话:ax+by=m要想有整数解,即x,y取值为整数,m必须为a,b最大公约数的倍数
这个定理证明请看百度,这里不加以赘述。
因为苹果和香蕉分别从两头相对而行,所以这道题可以抽象为 ax+by=m
问x
,y
有几个正整数取值。
将实际问题抽象为数学问题后就简单多了,现在只需要看x,y有几个正整数取值,几个就是答案。
下边是难点
注意自己画图体会
首先要注意的是上式中的m对应本题中的n+1,为什么是n+1而不是n呢?
很简单
我们上边式子是区间长度,而题目中a,b是点的坐标,坐标比区间个数多一,因此对于此题就是求解 ax+by=n+1
有多少组解
下面程序叙述太麻烦,不会的自行百度。。。。
其中exgcd函数
作用是求出x,y任意一组解并返回最大公约数
而x通解可以写成x+b/gcd * t
,y通解y-a/gcd *t
其中最大gcd为a,b的最大公约数,t为任意整数。
扩展欧几里得算法
#include <iostream>
#include <cstring>
using namespace std;
#define int long long
int exgcd(int a, int b, int &x, int &y)
{
if(!b)
{
x = 1, y = 0;
return a;
}
int res = exgcd(b, a % b, y, x);
y -= a / b * x;
return res;
}
signed main()
{
int n, a, b, x, y;
cin >> n >> a >> b;
n ++;
int d = exgcd(a, b, x, y);
int modx = b / d, mody = a / d;
if(n % d) cout << 0 << endl;//根据欧几里得定理,若不能整除最大公约数则无解
else
{
x *= n / d;
y *= n / d;
x = (x % modx + modx) % modx;//找出x的最小正整数解
if(!x) x += modx;
y = (n - a * x) / b;//x为最小正整数时y对应的值,y<0证明无解
if(y <= 0)cout << 0 << endl;//这里方程虽然有解,但是负数,不符合题意所以为0
else
{
int t = (y % mody != 0);//算y有几个取值,即答案为多少
cout << y / mody + t << endl;
}
}
}