前言:

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

扩展中国剩余定理:

浅谈扩展中国剩余定理_#include



解决形如以上方程的最小非负数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屠龙勇士

浅谈扩展中国剩余定理_#include_02

中间的转换很简单,最后会变成一系列同余方程,这时上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不难,还挺有趣。(又在水总结)