zoukankan      html  css  js  c++  java
  • 中国剩余定理学习笔记

    中国剩余定理

    中国剩余定理常用来求解同余方程组,形如

    [x equiv a_i pmod m_i ]

    的方程组


    首先,我们来讨论模数互质的:

    对于这类问题应该怎么求解呢?

    (果然我只是会背个板子)
    首先,我们定义

    [M=prod m_i ]

    然后令$$M_i = frac{M}{m_i}$$
    定义$$t_i为M_i 在 mod m_i意义下的逆元$$
    (这里求逆元可以使用exgcd来求)

    则最终的解就是

    [ans=sum_i{M_it_ia_i} ]

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #define ll long long
    
    using namespace std;
    
    inline ll read()
    {
      ll x=0,f=1;char ch=getchar();
      while (!isdigit(ch)) {if (ch=='-') f=-1; ch=getchar();}
      while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
      return x*f;
    }
    
    const int maxn = 210;
    
    ll m[maxn],a[maxn];
    ll n;
    ll ans;
    ll M=1;
    
    void exgcd(ll &x,ll &y,ll a,ll b)
    {
    	if (b==0)
    	{
    		x=1;y=0;
    		return;
    	}
    	exgcd(x,y,b,a%b);
    	int tmp = x;
    	x=y;
    	y=tmp-a/b*y;
    }
    
    void crt()
    {
       for (int i=1;i<=n;i++)
       {
       	  ll Mi=M/m[i];
       	  ll ti=0,y=0;
       	  exgcd(ti,y,Mi,m[i]);
       	  ans=(ans+Mi*ti%M*a[i]%M)%M;
       }
       while (ans<0)
       {
       	 ans+=M;
       }
    }
    
    int main()
    {
      n=read();
      for (int i=1;i<=n;i++) m[i]=read(),a[i]=read(),M=M*m[i];
      crt();
      cout<<ans<<endl;
      return 0;
    }
    
    

    扩展中国剩余定理

    那么如果模数不是互质的呢

    这时候就需要拓展CRT了

    对于

    [x equiv a_1 pmod {m_1} ]

    [x equiv a_2 pmod {m_2} ]

    它等价于

    [x=a_1+k_1m_1 ]

    [x=a_2+k_2m_2 ]

    联立之后,就能得到一个不定方程

    [k_1m_1-k_2m_2=a_2-a_1 ]

    根据裴蜀定理,我们知道如果(gcd(m_1,m_2) | (a_2-a_1)),那么这个方程就有整数解

    (k_1=frac{m_2}{g}t+k_1')

    设最小正整数解为(k_1')

    那么(x=a_1+k_1m_1=a_1+frac{m_2}{g}tm_1+k_1'm_1)

    我们设(a_1+k_1'm_1)为x_0

    那么(x=x_0+frac{m_1m_2}{gcd(m1,m2)}t)

    则新的方程就变成了$$x equiv x_0 pmod {lcm(m1,m2)}$$

    引入一道例题
    poj2891

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    
    using namespace std;
    
    inline long long read()
    {
      long long x=0,f=1;char ch=getchar();
      while (!isdigit(ch)) {if (ch=='-') f=-1; ch=getchar();}
      while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
      return x*f;
    }
    
    const int maxn = 1e6+1e2;
    
    long long m[maxn],a[maxn];
    long long M;
    long long ans;
    long long x0;
    long long gcd;
    int n;
    
    long long exgcd(long long &x,long long &y,long long a,long long b)
    {
    	if (b==0)
    	{
    		x=1;
    		y=0;
    		return a;
    	}
    	long long cnt=exgcd(x,y,b,a%b);
    	long long tmp = x;
    	x=y;
    	y=tmp-a/b*x;
    	return cnt;
    } 
    
    long long solve()
    {
        x0=a[1];//x0表示从第一个式子开始,合并到当前点的前一个时a是多少 
        M=m[1];//M同x0 
        long long x=0,y=0;
        for (int i=2;i<=n;i++)
        {
        	gcd=exgcd(x,y,M,m[i]);
        	if ((a[i]-x0)%gcd!=0) return -1;//判断不定方程的右边能不能整除gcd 
        	x=x*(a[i]-x0)/gcd;//扩大相应的倍数 
        	long long tmp = m[i]/gcd;
        	x=(x%tmp+tmp)%tmp;//根据特解公式,防止爆掉 
        	x0=x*M+x0;//求合并完的x0 
        	M=M*m[i]/gcd;
        	x0=x0%M;
    	}
    	x0=(x0+M)%M;
    	return x0;
    }
    int main()
    {
      while (scanf("%d",&n)!=EOF)
      {
      	for (int i=1;i<=n;i++)
      	  m[i]=read(),a[i]=read();
      	printf("%lld
    ",solve());
      }
      return 0;
    }
    
    
  • 相关阅读:
    CODEVS——T 2618 核电站问题
    Spring使用AspectJ注解和XML配置实现AOP
    oracle存储过程
    oracle什么时候需要commit
    短信发送接口被恶意访问
    JAVA内存模型
    构造函数,静态代码块,构造代码块
    mybatis缓存
    volatile和synchronized
    利用反射创建对象必须要显式的声明构造方法吗?
  • 原文地址:https://www.cnblogs.com/yimmortal/p/10160807.html
Copyright © 2011-2022 走看看