zoukankan      html  css  js  c++  java
  • 中国剩余定理及EX及单层EXLucas定理讲解

    NOIP前恶补数论的感觉真是神清气爽啊!

    教练问我们有什么知识点不会,一问到数论马上就GG了。

    花了2个小时学习中国剩余定理。

    好了废话少说,我们进入正题。

    首先膜拜一下中国南北朝时期的数学家孙子(不是孙武)。

    用现代数学的语言来说明的话,中国剩余定理给出了以下的一元线性同余方程组:

    求最小的正整数解x。

    当m1--mn相互互质时可以求解(若不互质可以通过扩展中国剩余定理求解)。

    分几步求解:

    1.求一下∏ni=1mi记为m

    2.设Mi=m/mi

    3.设ti满足Mi$ imes$ti≡1(mod mi)

    4.x=∑ni=1Mi$ imes$ai$ imes$ti

    至于第四步的原因

    因为我们保证Mi是除了当前mi之外所有模数的倍数,所以我们的第4步的理论依据是对于任意K(K≠i),Mi$ imes$ai$ imes$ti ≡ 0(mod mk),所以Mi$ imes$ai$ imes$ti≡ai(mod mi).所以第4步成立。

    问题来了,Mi和ai好办,如何求ti

    我们注意到Mi$ imes$ti≡1(mod mi)。考虑扩展GCD,把它转换成ax+by=1解同余方程。

    void exgcd(ll a,ll b,ll &x,ll &y)
    {
    	if(!b)
    	{
    		x=1;
    		y=0;
    		return;
    	}
    	exgcd(b,a%b,x,y);
    	ll tmp=x;
    	x=y,y=tmp-a/b*y;
    	return ;
    }
    

    解完之后,就可以求辣!

    ll China()
    {
    	ll x,ans=0,y;
    	for(int i=1;i<=n;i++)
    		m*=in[i];
    	for(int i=1;i<=n;i++)
    	{
    		M[i]=m/in[i];
    		exgcd(M[i],in[i],x,y);
    		ans=(ans+a[i]*x*M[i])%m;
    	}
    	return (ans+m)%m;
    }
    

    中国剩余定理最基础题目的网站https://neooj.com:8082/oldoj/problem.php?id=1322  

    上代码

    #include<cstdio>
    typedef long long ll;
    ll M[11];
    ll t[11];
    ll a[11];
    ll in[11];
    ll m=1;
    ll n;
    void exgcd(ll a,ll b,ll &x,ll &y)
    {
    	if(!b)
    	{
    		x=1;
    		y=0;
    		return;
    	}
    	exgcd(b,a%b,x,y);
    	ll tmp=x;
    	x=y,y=tmp-a/b*y;
    	return ;
    }
    ll China()
    {
    	ll x,ans=0,y;
    	for(int i=1;i<=n;i++)
    		m*=in[i];
    	for(int i=1;i<=n;i++)
    	{
    		M[i]=m/in[i];
    		exgcd(M[i],in[i],x,y);
    		ans=(ans+a[i]*x*M[i])%m;
    	}
    	return (ans+m)%m;
    }
    int main()
    {
    	scanf("%lld",&n);
    	for(int i=1;i<=n;i++)
    		scanf("%lld%lld",&in[i],&a[i]);
    	ll ans=China();
    	printf("%lld",ans);
    }
    

    下面讲解扩展Lucas定理。

    普通的Lucas定理长这样: 

    C(n,m)%p=C(n/p,m/p)*C(n%p,m%p)%p(p为质数)

    上个代码

    #include<cstdio>
    #define mod 10007
    int fac[mod+2];
    int inv[mod+2];
    int lucas(long long n,long long m)
    {
        if(n<m)
            return 0;
        if(n<mod&&m<mod)
            return fac[n]*inv[m]%mod*inv[n-m]%mod;
        return lucas(n%mod,m%mod)*lucas(n/mod,m/mod)%mod;
    }
    int main()
    {
        fac[0]=1,inv[mod-1]=mod-1;
        for(int i=1;i<=mod;i++) fac[i]=fac[i-1]*i%mod;
        for(int i=mod-2;i>=0;i--) inv[i]=inv[i+1]*(i+1)%mod;
        long long n,m;
        scanf("%I64d%I64d",&n,&m);
        printf("%d
    ",lucas(n,m));
    }  

    对于p不是质数的情况我们可以向中国剩余定理的方向讨论。

    若不是素数,将p分解质因数,将C(n,m)分别按照Lucas的方法求对p的质因数的模,然后用中国剩余定理合并。比如计算C(10,3)%14。C(10,3)=120,14有两个质因数2和7,120%2=0,120%7=1,这样用(2,0)(7,1)找到最小的正整数8即是答案,即C(10,3)%14=8非常简单。注意,这里只适用于p分解完质因数后每个质因数只出现一次.至于多次的,我不会(大写GG,有兴趣的可以学习一下.)

    EX中国剩余定理

    因为模数不是质数,所以我们采用两两合并的思想。

    x=a1+m1$ imes$y1

    x=a2+m2$ imes$y2

    两方程联立,x≡a1+m1$ imes$y1(mod lcm(m1,m2))

    蒋神blog中有证明

    https://www.cnblogs.com/ShuraK/p/7905790.html##11

    上code:P4777 【模板】扩展中国剩余定理(EXCRT)

    ll exgcd(ll a,ll b,ll &x,ll &y)
    {
        if(!b)
        {
            x=1;
            y=0;
            return a;
        }
        ll gcd=exgcd(b,a%b,x,y);
        ll tmp=x;
        x=y;
        y=tmp-a/b*y;
        return gcd;
    }
    ll pow(ll x,ll y,ll mod)
    {
        ll ans=0;
        while(y)
        {
            if(y&1)
                ans=(ans+x)%mod;
            x=(x+x)%mod;
            y/=2;
        }
        return ans;
    }
    ll China()
    {
        ll M=m[1];
        ll ans=a[1];
        for(int i=2;i<=n;i++)
        {
            ll A=M,B=m[i],d=((a[i]-ans)%B+B)%B,x,y;
            ll gcd=exgcd(A,B,x,y);
            x=pow(x,d/gcd,B/gcd);
            M*=B/gcd;
            ans=(ans+x*A)%M;
        }
        return (ans%M+M)%M;
    }
    

      

  • 相关阅读:
    数组元素的查找1
    排序——选择排序
    排序——冒泡排序
    内部类——匿名内部类
    跳一跳
    数组元素换位置游戏
    六 java和Tomcat
    九 Jenkins持续集成
    八 ip和子网详解
    七 Git版本控制
  • 原文地址:https://www.cnblogs.com/342zhuyongqi/p/9845623.html
Copyright © 2011-2022 走看看