• 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;
                }
            }
        }
    }
    
    
    
     
  • 相关阅读:
    MongoDB:数据库管理
    MongoDB:用户管理
    MongoDB:入门
    彻底透析SpringBoot jar可执行原理
    轻松了解Spring中的控制反转和依赖注入(一)
    领域驱动最佳实践--用代码来告诉你来如何进行领域驱动设计
    血的教训--如何正确使用线程池submit和execute方法
    领域驱动设计之实战权限系统微服务
    为什么我们需要领域驱动设计
    【Go入门学习】golang自定义路由控制实现(二)-流式注册接口以及支持RESTFUL
  • 原文地址:https://www.cnblogs.com/xiongtao/p/9366459.html
Copyright © 2020-2023  润新知