浅谈扩展中国剩余定理

前言:

在本章会介绍扩展中国剩余定理,至于为什么不介绍中国剩余定理,因为我懒,而且我认为扩展定理可以解决一切。

扩展中国剩余定理:

浅谈扩展中国剩余定理

 

 

 解决形如以上方程的最小非负数x。

解决步骤简要分析:

设前k-1个方程解出的答案为ans,前k-1个m的lcm=M,则新的ans为(ans+M*x),且(ans+M*x)≡ak(mod mk)。

这里的x为一个系数,之所以是M*x是因为满足当前的条件同时,还要满足之前的条件。所以就是M*x≡ak-ans(mod mk),那么转换为M*x+mk*y=ak-ans。那么这个式子就是个扩欧了,直接求出x,新的ans=ans+x*M。下面给出代码:

#include<bits/stdc++.h>
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define ll long long
using namespace std;
ll read()
{
    ll out = 0,flag = 1;char c = getchar();
    while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
    while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
    return flag * out;
}
int k;
ll a[100010],b[100010];
ll exgcd(ll a,ll b,ll &x,ll &y)
{
    if(!b)
    {
        x=1;
        y=0;
        return a;
    }
    ll d=exgcd(b,a%b,x,y);
    ll t=x;
    x=y;
    y=t-a/b*y;
    return d;
}
ll mul(ll A,ll B,ll mod)//快速乘
{
    ll ans=0;
    while(B>0)
      {
        if(B & 1) ans=(ans+A%mod)%mod;
        A=(A+A)%mod;
        B>>=1;
      }
    return ans;
}
int main()
{
    k=read();
    fo(i,1,k)
    {
        scanf("%lld%lld",&b[i],&a[i]);
    }
    fo(i,1,k) a[i]=(a[i]+b[i])%b[i];///预处理
    ll ans=0,lcm=1,A,B,C,gcd,x,y;
    fo(i,1,k)
    {
        A=lcm;
        B=b[i];
        C=(a[i]-ans%b[i]+b[i])%b[i];
        ll gcd=exgcd(A,B,x,y);
        if(C%gcd>0)
        {
            return 0;
        }
        x=mul(x,C/gcd,b[i]);
        ans+=lcm*x;
        lcm=lcm/gcd*b[i];
        ans=(ans%lcm+lcm)%lcm;
    }
    printf("%lld",ans);
    return 0;
}

结合代码应该会更好的理解。

下面给出一道例题:

4774屠龙勇士

浅谈扩展中国剩余定理

中间的转换很简单,最后会变成一系列同余方程,这时上exrct就行了。

:最后求出的ans不一定就是最后的ans,因为ans还要满足将每个龙杀死,所以在处理的时候要注意这一点。

在这里引出一个模型,有n个苹果,每个篮子可以装m个苹果,那么需要(n-1)/m+1个篮子才能保证装完。

下面代码会有2处体现这个模型,放代码:

#include<bits/stdc++.h>
#define ll long long
#define fo(i,j,k) for(int i=j;i<=k;i++)
using namespace std;
const int maxn=100005;
int T,n,m,b[maxn],t[maxn];
long long a[maxn],p[maxn],mx;
multiset<long long> s;
multiset<ll>::iterator it;
ll exgcd(ll a,ll b,ll &x,ll &y)
{
    if(!b)
    {
        x=1;
        y=0;
        return a;
    }
    ll d=exgcd(b,a%b,x,y);
    ll tx=x;
    x=y;
    y=tx-a/b*y;
    return d;
}
ll excrt()
{
    ll ans=0,lcm=1,x,y,gcd,A,B,C;
    fo(i,1,n)
    {
        A=(__int128)b[i]*lcm%p[i];
        B=p[i];
        C=(a[i]-b[i]*ans%p[i]+p[i])%p[i];
        gcd=exgcd(A,B,x,y);
        x=(x%p[i]+p[i])%p[i];
        if(C%gcd) return -1;
        ans+=(__int128)(C/gcd)*x%(B/gcd)*lcm%(lcm*=B/gcd);
        ans%=lcm;
    }
    if(ans<mx) ans+=((mx-ans-1)/lcm+1)*lcm;//第二处
    return ans;
}
int main()
{
    cin>>T;
    while(T--)
    {
        s.clear(),mx=0;
        cin>>n>>m;
        fo(i,1,n) cin>>a[i];
        fo(i,1,n) cin>>p[i];
        fo(i,1,n) cin>>t[i];
        fo(i,1,m) 
        {
            int x;
            cin>>x;
            s.insert(x);
        }
        fo(i,1,n)
        {
            it = s.upper_bound(a[i]);
            if(it!=s.begin()) it--;
            b[i]=*it;
            s.erase(it);
            s.insert(t[i]);
            mx=max(mx,(a[i]-1)/b[i]+1);//第一处
        }
        cout<<excrt()<<endl;
    }
    return 0;
}

通过这个代码让我学习到了multiset,真是个好东西啊。

multiset<long long> s;
multiset<ll>::iterator it;
it = s.upper_bound(a[i]);
            if(it!=s.begin()) it--;
            b[i]=*it;
            s.erase(it);
            s.insert(t[i]);

这一段的语法可以记一记,讲实话stl真是我的一大痛点啊。

总结:

excrt不难,还挺有趣。(又在水总结)

 

上一篇:CF 1542C C. Strange Function


下一篇:最大公约数GCD&最小公倍数LCM