POJ2891 中国剩余定理推论+归纳思想

POJ2891 中国剩余定理推论+归纳思想

题面
POJ2891题面

思路
归纳思想
这种思想真的不知道用在多少算法上面了(递推(还记得汉诺塔怎么做的吗),递归,dp,fjord……),虽然有时很难理解,但是用这种方法思考问题的方式真的应该成为每一个ACMer思考问题的方式之一,解决离散问题的首选……
对于方程
{ x ≡ a 1 ( m o d   m 1 ) x ≡ a 2 ( m o d   m 2 ) ⋮ x ≡ a n ( m o d   m n ) (1) \begin{cases} x \equiv a_1(mod\space m_1)\\ x \equiv a_2(mod\space m_2)\\ \vdots \\ \tag{1} x \equiv a_n(mod\space m_n) \end{cases} xa1(mod m1)xa2(mod m2)xan(mod mn)(1)
假设我们已经知道了前k个方程的一个解 x x x,那么这时还是取
m = l c m ( m 1 , m 2 , ⋯   , m k ) (2) m = lcm(m_1,m_2,\cdots,m_k) \tag{2} m=lcm(m1,m2,,mk)(2)
那么对于(1)中的前k个的mod,m肯定取模都是0的(或者说 m i ∣ m , i ∈ [ 1 , k ] m_i\mid m,i\in[1,k] mim,i[1,k]),所以通解肯定是
x + p ∗ m ( p ∈ Z ) x+p*m(p\in Z) x+pm(pZ),那么现在想在其中找一个解(就是找一个p)满足第k+1个方程 x + p ∗ m ≡ a k + 1 ( m o d   m k + 1 ) x+p*m \equiv a_{k+1}(mod\space m_{k+1}) x+pmak+1(mod mk+1),即 p ∗ m ≡ a k + 1 − x ( m o d   m k + 1 ) p*m \equiv a_{k+1}-x(mod\space m_{k+1}) pmak+1x(mod mk+1),找出 a k + 1 − x ( m o d   m k + 1 ) a_{k+1}-x(mod\space m_{k+1}) ak+1x(mod mk+1)的最小正整数 q q q,然后转化对应的Eulicd方程 p ∗ m + y ∗ m k + 1 = q p*m+y*m_{k+1}=q pm+ymk+1=q,还是用exgcd,求出 g c d ( m , m k + 1 ) gcd(m,m_{k+1}) gcd(m,mk+1)判断能不能整除q(否则无解)(是不是和刚刚求得gcd很像),有解求出p,这时就得到了前k+1个方程的一个解 x + p ∗ m x+p*m x+pm,归纳完毕
为了最后导出最小整数解,要把结果对 l c m ( m 1 , m 2 , ⋯   , m n ) lcm(m_1,m_2,\cdots,m_n) lcm(m1,m2,,mn)取模
这个过程实际上就是递归生成法一中的ans的过程

注意事项
1)这里涉及的变量很多,最好把演算时的变量和程序中的变量同名,不然很容易乱
2)一定要注意int和long long 的上限,像这种取模运算题,不要用*=,+=这类运算符

复习反馈
1)没有理解 a x + b y = c ax+by=c ax+by=c的本质,用exgcd解出x特解 x 0 x_0 x0后,此时是 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b)的方程的解,显然 x = x 0 + k ∗ b ( k ∈ Z ) x = x_0+k*b(k\in Z) x=x0+kb(kZ),但是对于原方程的解即使是放大倍数仍然是以b为循环节,也就是说所得的解 a n s = x 0 ∗ c / g c d ( a , b ) ( m o d   b ) ans = x_0*c/gcd(a,b)(mod\space b) ans=x0c/gcd(a,b)(mod b)
2)还是没有理解这里的归纳的本质,虽然对于前k个方程求完解之后可以对x取模求最小,但是用来求和的变量只有在最后一步才能取模,否则就失去了x+t*lcm的解的形式,下一步的t也就没法解了
代码

#include <iostream>

using namespace std;
typedef long long ll;
ll x,y;
ll exgcd(ll a,ll b,ll &x,ll &y)
{
    if(!b){
        x=1;y=0;return a;
    }
    else
    {
        ll d = exgcd(b,a%b,x,y);
        ll z = x;x = y; y = z - (a/b)*y;
        return d;
    }
}
int main()
{
    int kase;
    cin >>kase;
    ll a,b,m,ans;bool flag = true;
    for(int i = 0;i <kase;i++)
    {
        
        cin >> a >> b;//a mod // b remindar
        if(i == 0) m = a,ans = b;
        else
        {
            ll ak = (b-ans%a+a)%a;
            ll tmp = exgcd(m,a,x,y);
            x = ((x%a)+a)%a;
            if(ak%tmp) {flag = false;break;}
            x=x*(ak/tmp)%a;
            ans += x*m;
            //m *= a;m/=tmp;
            m = m /tmp*a;
            ans = (ans%m+m)%m;
        }
        
    }
    if(!flag) ans = -1;    
    cout <<ans <<endl;
    return 0;
    
}
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值