51nod1352 集合计数(扩展欧几里得)

题目链接:http://www.51nod.com/Challenge/Problem.html#!#problemId=1352


思路:题目就是求ax+by=n+1,有几个符合的(x,y)。

首先找到a,b的最大公约数d,n+1是d的倍数才有符合的解。
我们可以简单证明下:因为d是a,b的最大公约数,设a=k1*d,b=k2*d(k1,k2为正整数).
所以a*(k1*d)+b*(k2*d)=n+1,即(n+1)/d=a*k1+b*k2,所以n+1一定要是最大公约数的倍数才有解

 先用算出a*x+b*y=n+1 的最小正数x解,如果此时y不是正数则没有解答案是0,如果是正数则答案是y/(a/gcd(a,b))(+1)

(如果y是a/gcd(a,b)的倍数不加1,不是就加1,可以这样理解y本身是一个解,如果整除,y的解包含进去了,不整除,y这个解没加)

 也可以上下同时乘以b,   写成b*y/(a*b/gcd(a,b))又因为a*b/gcd(a,b)就是a,b的最小公倍数,所以又可以写成b*y/lcm(a,b)(+1)(lcm(a,b)的最小公倍数)

 

为什么答案是y/(a/gcd(a,b)(+1)呢?

 因为(x,y)是一组解,则(x+k*b/gcd(a,b),y-k*a/gcd(a,b))(k为正整数)也是解。自己可以动手写就知道了

 为什么是除以a/gcd(a,b)不是除以a呢?因为是除a解多还是除a/gcd(a,b)多?我也想了半天才知道。。。。笑笑自己弱鸡

 

哪最小正数解x怎么算呢?

 我们只要用扩展欧几里得模板算出a*x+b*y=gcd(a,b) 的一个解x 再乘以(n+1)/gcd(a,b)就可以了得出a*x+b*y=n+1的一个x解,

 但是此时x有可能不是最小,也有可能是负数,怎么办?

 由上面我们可以知道a*(x+b/gcd(a,b))+b*(y-a/gcd(a,b))=n+1 每b/gcd(a,b)有一个x解,只要x对b/gcd(a,b)取模就行了,

 再判断是负数就加b/gcd(a,b)就是最小正数解了。

代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
ll exgcd(ll a,ll b,ll &x,ll &y)
{
    if(b==0)
    {
        x=1;
        y=0;
        return a;
    }
    ll ans=exgcd(b,a%b,x,y);
    ll t=x; 
    x=y; 
    y=t-(a/b)*y;
    return ans;
}

int main()
{
    ll t;
    cin>>t;
    ll n,a,b;
    while(t--)
    {
        cin>>n>>a>>b;
        ll x,y;
        ll d=exgcd(a,b,x,y);//a,b的最大公约数
        //cout<<x<<" "<<y<<endl; 
        ll lcm=a*b/d;
        if((n+1)%d)
            cout<<"0"<<endl;
        else
        {
            x=x*(1+n)/d;
            ll r=b/d;
            x=x%r;
            while(x<=0) 
                x+=r;//最小的正整数解x 
            ll s=n+1-x*a;//s为b*y 
            if(s<0)
                cout<<"0"<<endl;
            else
            {
                if(s%a)
                    cout<<1+s/lcm<<endl;
                else
                    cout<<s/lcm<<endl;
            }
        }
    }
}

 

 
posted @ 2018-07-25 17:29  怀揣少年梦.#  阅读(282)  评论(0编辑  收藏  举报