zoukankan      html  css  js  c++  java
  • 浅谈中国剩余定理

    我们先来看这样一道题目:

    树王种了一棵treap,她现在决定把这棵treap改造为一棵无旋多叉triep,于是她摘下了treap的所有节点,发现如果她把节点3个3个一打包,会剩下2个节点。如果她把节点5个5个一打包,会剩下3个节点,如果把节点7个7个一打包,会剩下2个节点,求这棵treap最少有多少节点?

    简化后题目就是,已知x%3=2,,x%5=3,x%7=2,求x最小值?

    如何解决呢?我们设k1,k2,k3满足 k1%3=1,k1%5=0,k1%7=0,k2%5=1,k2%3=0,k2%7=0.k3%7=1.,k3%3=0,k3%5=0.

    那么 k1*2+k2*3+k3*2一定是满足答案的一个解。(网上说容易意会,容易意会个鬼啊)

    证明: 设x=k1*2+k2*3+k3*2。 则x%3=(k1*2+k2*3+k3*2)%3=(k1*2%3+k2*3%3+k3*2%3)%3=(2+0+0)%3=2;

    其他两组解同理。

    那么如何求k1,k2,k3?

    我们求出LCM(3,5,7)=105,设x1=LCM(3,5,7)/3=35,x2==LCM(3,5,7)/5=21,x3==LCM(3,5,7)/7=15.

    那么设k1=n1*x1,k2=n2*x2,k3=n3*x3.

    k1%3=1,k2%5=1,k3%7=3,可转换为 n1*x1%3=1,n2*x2%5=1,n3*x3%7=1.

    然后可以用扩展欧几里得求出n1,n2,n3.进而得到 k1,k2,k3.

    最后便可得到答案,而题目的通解可表示为这个数每次都加上3,5,7的最小公倍数。

    好了,现在步入正题(你没看错现在我才开始讲正经的,刚才那一堆都是铺垫)

    已知   x≡a[1](mod)y[1],x≡a[2](mod)y[2],x≡a[3](mod)y[3].........x≡a[n](mod)y[n].

    其中 y[1],y[2],y[3]......y[n]两两互质,求x

    其算法流程如下。

    1.计算LCM(y1,y2......yn)。

    2.从1->n,计算f[i]=LCM(y[1[,y[2],....y[n])/y[i].

    3.使用扩展欧几里得定理,对同余式b[i]*f[i]%y[i]=1,计算出b[i],进而得出k[i]=b[i]*f[i].

    4.从1->n,计算x=k[1]*a[1]+k[2]*a[2]+...........k[n]*a[n].

    5.对x模lcm,得到答案。

     接下来给出代码:

    #include <bits/stdc++.h>
    #define maxn 105
    using namespace std;
    int ex_gcd(int a,int b,int &x,int &y)//扩展欧几里得定理,解ax+by=c。
    {
        if(b==0)
        {
            x=1;
            y=0;
            return a;
        }
        int ans=ex_gcd(b,a%b,x,y);
        int temp=x;
        x=y;
        y=temp-(a/b)*x;
        return ans;
    }
    int a[maxn],b[maxn],n,f[maxn];
    int solve()
    {
        int ans=0;
        int lcm=1;
        for(int i=1;i<=n;i++)
        {
            lcm*=a[i];//两两互质,直接乘了
        }
        for(int i=1;i<=n;i++)
        {
            f[i]=lcm/a[i];
        }
        for(int i=1;i<=n;i++)
        {
            int x,y,g;
            g=ex_gcd(f[i],a[i],x,y);
            x=(x%a[i]+a[i])%a[i];//因为扩展欧几里得求出的解可能有很多组,且可能为负,我们这步求出了一个比a[i]小且大于0的解。
            ans=(ans+x*f[i]*b[i])%lcm;
        }
        return ans;
    }
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&a[i],&b[i]);//ans%a[i]=b[i].
        }
        int ans=solve();
        printf("%d
    ",ans);
    }
    

     接下来我们谈谈扩展中国剩余定理

    今天才被扩展欧拉定理gank的我来学习扩展中国剩余定理了

    已知   x≡a[1](mod)y[1],x≡a[2](mod)y[2],x≡a[3](mod)y[3].........x≡a[n](mod)y[n],求x。

    现在y[1],y[2]........y[n]不互质了,一般情况的中国剩余定理不适用了,这怎么搞?

    考虑 两个方程,分别为x≡a[1](mod)y[1],x≡a[2](mod)y[2].

    则有x=y[1]*n1+a[1],x=y[2]*n2+a[2].

    合并得 y[1]*n1+a[1]=y[2]*n2+a[2].化简:   y[1]*n1-y[2]*n2=a[2]-a[1].

    使用扩展欧几里得定理 解除最小正整数解 n1,设k=y[1]*n1+a[1].

    那么存在通解x=k+t*lcm(y[1],y[2]).有 x≡k(mod)lcm(y[1],y[2])。

    一路合并下去就ok了。

    接下来给出例题 POJ2891代码

    传送门:Strange Way to Express Integers

    #include <cstdio>
    #define maxn 100005
    using namespace std;
    typedef long long ll;
    ll ex_gcd(ll a,ll b,ll &x,ll &y)//扩展欧几里得定理,解ax+by=c。
    {
        if(b==0)
        {
            x=1;
            y=0;
            return a;
        }
        ll ans=ex_gcd(b,a%b,x,y);
        ll temp=x;
        x=y;
        y=temp-(a/b)*x;
        return ans;
    }
    ll a[maxn],b[maxn],n,f[maxn];
    ll China()
    {
        ll mod=a[1],reminder=b[1];//模数为mod,余数为reminder。
        for(int i=2;i<=n;i++)
        {
            ll x,y,g,temp;
            g=ex_gcd(mod,a[i],x,y);
            temp=b[i]-reminder;
            if(temp%g!=0)
            {
                return -1;
            }
            x=x*temp/g;
            ll t=a[i]/g;
            x=(x%t+t)%t;
            reminder=x*mod+reminder;
            mod=mod/g*a[i];
            reminder%=mod;
        }
        reminder=(reminder%mod+mod)%mod;
        return reminder;
    }
    int main()
    {
        while(~scanf("%lld",&n))
        {
            for(ll i=1;i<=n;i++)
            {
                scanf("%lld%lld",&a[i],&b[i]);
            }
            printf("%lld
    ",China());
        }
    }
    

      

  • 相关阅读:
    python 学习之集合 三元运算 深浅拷贝
    python 学习之数据类型和for循环
    python 学习之运算符
    python 学习之编码转换和pycharm设置
    python 学习之python数据类型和流程控制
    Django实现下载文件名称为中文的处理
    递归删除postgresql数据库中所有表
    GO编程(打卡)-Task13: 并发编程
    GO编程(打卡)-Task12: 单元测试
    GO编程(打卡)-Task11: 反射机制
  • 原文地址:https://www.cnblogs.com/zyf3855923/p/9748657.html
Copyright © 2011-2022 走看看