II.exCRT(扩展中国剩余定理)
上文我们说到,CRT仅适用于 \(m\) 两两互质的情形。那如果不保证这一限制,明显原方程是仍然有解的,如何求解呢?
在上文的最后,我们成功将三个式的方程消到了两个,在这里能否继续?
我们考虑这个式子:
其等价于 \(\alpha A-\beta B=b-a\)。\(A,B,b-a\) 均为常数。
发现了什么?这不是exGCD的形式吗?
于是我们用exGCD求出一组解 \(\alpha,\beta\),然后就把两个式子的方程给压到了一个。
(附:实际上,其还等价于 \(\alpha A\equiv b-a\pmod{B}\),进一步等价得 \(\alpha\dfrac{A}{\gcd}\equiv\dfrac{b-a}{\gcd}\pmod{\dfrac{B}{\gcd}}\))
代码实现时需要非常小心,要保证每个东西都模上了它所能模的最小的东西。有些地方必须用快速乘来保证不爆 long long
。
II.I.【模板】扩展中国剩余定理(EXCRT)
就是模板啦。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n;
ll a[100100],m[100100];
ll exGCD(ll a,ll b,ll &x,ll &y){
if(!b){x=1,y=0;return a;}
ll tmp=exGCD(b,a%b,y,x);y-=a/b*x;return tmp;
}
ll ksc(ll x,ll y,ll mod){
ll z=0;
for(;y;y>>=1,x=(x<<1)%mod)if(y&1)(z+=x)%=mod;
return z;
}
void exCRT(int i){//merge equation i and i+1
ll A=m[i],B=m[i+1],alpha,beta,c=(a[i+1]-a[i]%B+B)%B;
ll gcd=exGCD(A,B,alpha,beta);
ll newb=B/gcd;
// printf("%lld %lld %lld %lld:%lld\n",A,alpha,B,beta,gcd);
// printf("%lld %lld\n",a[i],a[i+1]);
alpha%=newb;if(alpha<0)alpha+=newb;
alpha=ksc(alpha,c/gcd,newb);
m[i]*=newb;
(a[i]+=ksc(alpha,A,m[i]))%=m[i];
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%lld%lld",&m[i],&a[i]);
for(int i=n-1;i;i--)exCRT(i);
printf("%lld\n",a[1]);
return 0;
}
II.II.[NOI2018] 屠龙勇士
首先,可以轻松发现,每条龙用来砍的剑是唯一的。故设这把剑的攻击力为 \(atk_i\)。
先不管砍 \(x\) 刀后龙的血量是否会到达负数,明显一个合法的 \(x\) 必满足 \(a_i-atk_i\times x\equiv 0\pmod{p_i}\)。
其等价于 \(kp_i+atk_i\times x=a_i\)。是exGCD的形式,直接扩欧一波带走。
明显,在扩欧的形式下,这合法的 \(x\) 应有 \(x\equiv rm_i\pmod{md_i}\),其中 \(rm_i\) 与 \(md_i\) 可以直接由上式求出。
于是就回到了我们熟悉的exCRT的形式。
需要注意的是,在最终求出了符合条件的 \(x\) 应有 \(x\equiv A\pmod M\) 时,要注意 \(A\) 可能不是符合题意的解,可能要往上面加若干个 \(M\) 才能将所有龙的血量砍到负数。具体就直接对每条龙需要砍的次数取一个 \(\max\),然后找到比此 \(\max\) 更大的 \(x\) 即可。
另外,在求 \(atk_i\) 的时候,应该要用到一个 multiset
。如果你像我一样,看到攻击力都在 int
范围内就套了个 int
进去的话是不行的,因为你在里面 upper_bound
的时候,如果你传进去的是 long long
的话,STL
会自动强转成 int
然后再 upper_bound
!因此,只能选择在里面套上 long long
。
时间复杂度 \(O(n\log n)\)。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int T,n,m;
ll a[100100],p[100100],atk[100100],bon[100100];
multiset<ll>s;
ll exGCD(ll a,ll b,ll &x,ll &y){
if(!b){x=1,y=0;return a;}
ll tmp=exGCD(b,a%b,y,x);y-=a/b*x;return tmp;
}
ll rm[100100],md[100100];
ll ksc(ll x,ll y,ll mod){
ll z=0;
for(;y;y>>=1,(x<<=1)%=mod)if(y&1)(z+=x)%=mod;
return z;
}
bool prep(){
for(int i=1;i<=n;i++){
ll A=atk[i],B=p[i],C=a[i],u,v;
ll gcd=exGCD(A,B,u,v);
if(C%gcd)return false;
ll np=B/gcd;
u%=np;if(u<0)u+=np;
u=ksc(u,(C/gcd)%np,np);
rm[i]=u,md[i]=np;
}
return true;
}
ll exCRT(){
ll X=0,M=1;
for(int i=1;i<=n;i++){
ll A=M,B=md[i],C=(rm[i]-X%B+B)%B,x,y;
ll gcd=exGCD(A,B,x,y);
if(C%gcd)return -1;
ll np=B/gcd;
x%=np;if(x<0)x+=np;
x=ksc(x,(C/gcd)%np,np);
M*=np;
(X+=ksc(x,A,M))%=M;
}
// printf("%lld %lld\n",X,M);
ll tms=0;
for(int i=1;i<=n;i++)if(X*atk[i]<a[i])tms=max(tms,((a[i]-1)/atk[i]-X)/M+1);
return X+tms*M;
}
int main(){
// freopen("P4774_18.in","r",stdin);
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
for(int i=1;i<=n;i++)scanf("%lld",&p[i]);
for(int i=1;i<=n;i++)scanf("%lld",&bon[i]);
for(int i=1,x;i<=m;i++)scanf("%lld",&x),s.insert(x);
for(int i=1;i<=n;i++){
set<ll>::iterator it;
if(*s.begin()>a[i])it=s.begin();else it=--s.upper_bound(a[i]);
atk[i]=*it,s.erase(it);
s.insert(bon[i]);
}
s.clear();
if(!prep()){puts("-1");continue;}
// for(int i=1;i<=n;i++)printf("(%lld,%lld,%lld)\n",a[i],atk[i],p[i]);
// for(int i=1;i<=n;i++)printf("%lld %lld\n",rm[i],md[i]);
printf("%lld\n",exCRT());
}
return 0;
}