Description
Solution
一开始就想到了扩展欧几里得。
但是发现都要是非负整数,所以直接做不行。然后枚举一下n的系数,直接算出m的系数,虽然这是错误的方法,但是有50分。
用上面的方法ans=∑⌊qn⌋x=0q−x∗nm+(x!=0)
因为x=0的时候y不能等于0,所以x=0不能+1。
这样做其实是又重复的情况。
但是确定一下枚举的边界就行了。
ans=∑⌊ngcd(n,m)⌋x=0q−x∗nm+(x!=0)
为什么ngcd(n,m)就是边界呢?
设x0和y0是方程初始的一组根。
扩展欧几里得的通解是这样的。
x=x0+k∗⌊mgcd(n,m)⌋,y=y0−k∗⌊ngcd(n,m)⌋
要找x的最小解:
把x mod 一下,如果x小于0,那么再加一下。
那么xmod⌊mgcd(n,m)⌋是在[0,⌊mgcd(n,m)⌋−1]的范围内的。
所以得证。
另一个方法
设xn+ym=c,设ym=an+i,i∈[0,n−1]。
那么转化一下n∗(a+x)+i=c
那么只要i不重复,就可以求出不重复的解。
所以对于i要找出最小的ym,使得ym=an+i。
设d[i]=ym表示最小的ym=an+i,因为d[(d[i]+m)modm]≤d[i]+m,根据这条式子,可以用spfa跑一次最短路来预处理出所有的d[i]。
最后ans=∑n−1x=0q−d[x]n。(这里就是找出所有的a)
Code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
typedef long long ll;
using namespace std;
ll i,j,k,l,t,n,m,ans,q,cas;
ll gcd(ll x,ll y){return (!y)?x:gcd(y,x%y);}
int main(){
freopen("simple.in","r",stdin);
freopen("simple.out","w",stdout);
// freopen("fan.in","r",stdin);
for(scanf("%lld",&cas);cas;cas--){
scanf("%lld%lld%lld",&n,&m,&q);
if(n<m)swap(n,m);
ans=q;
fo(i,0,m/gcd(n,m)-1){
if(n*i>q)break;
ans=(ans-(q-n*i)/m);
if(i)ans--;
}
printf("%lld\n",ans);
}
}