扩展中国剩余定理学习笔记

II.exCRT(扩展中国剩余定理)

上文我们说到,CRT仅适用于 \(m\) 两两互质的情形。那如果不保证这一限制,明显原方程是仍然有解的,如何求解呢?

在上文的最后,我们成功将三个式的方程消到了两个,在这里能否继续?

我们考虑这个式子:

\[x=a+\alpha A=b+\beta B \]

其等价于 \(\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;
}

posted @ 2021-04-06 11:01  Troverld  阅读(62)  评论(0编辑  收藏  举报