【NOIP2016提高A组集训第9场11.7】Simple

Description

【NOIP2016提高A组集训第9场11.7】Simple_第1张图片

Solution

一开始就想到了扩展欧几里得。
但是发现都要是非负整数,所以直接做不行。然后枚举一下n的系数,直接算出m的系数,虽然这是错误的方法,但是有50分。
用上面的方法 ans=qnx=0qxnm+(x!=0)
因为x=0的时候y不能等于0,所以x=0不能+1。
这样做其实是又重复的情况。
但是确定一下枚举的边界就行了。
ans=ngcd(n,m)x=0qxnm+(x!=0)
为什么 ngcd(n,m) 就是边界呢?
设x0和y0是方程初始的一组根。
扩展欧几里得的通解是这样的。
x=x0+kmgcd(n,m),y=y0kngcd(n,m)
要找x的最小解:
把x mod 一下,如果x小于0,那么再加一下。
那么 xmodmgcd(n,m) 是在 [0,mgcd(n,m)1] 的范围内的。
所以得证。

另一个方法

xn+ym=c ,设 ym=an+ii[0,n1]
那么转化一下 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=n1x=0qd[x]n 。(这里就是找出所有的a)

Code

#include
#include
#include
#include
#include
#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(n0,m/gcd(n,m)-1){
            if(n*i>q)break;
            ans=(ans-(q-n*i)/m);
            if(i)ans--;
        }
        printf("%lld\n",ans);
    }
}

你可能感兴趣的:(noip,扩展欧几里得,数论,最短路)