NOI 2018 屠龙勇士(exgcd,excrt)

题目
对于方程 a x ≡ b ( m o d p ) ax \equiv b \pmod p axb(modp)
gcd ⁡ ( a , p ) = g \gcd(a,p) = g gcd(a,p)=g
则求出 a x ≡ g ( m o d p ) ax \equiv g \pmod p axg(modp)的一组特解 x 0 x_0 x0后。
b   m o d   g ≠ 0 b \bmod g \neq 0 bmodg=0
则无解。
否则原方程等价于 x ≡ x 0 b g ( m o d p g ) x \equiv \frac {x_0b}g \pmod {\frac pg} xgx0b(modgp)

对于同余方程组 x ≡ a ( m o d c ) , x ≡ b ( m o d d ) x\equiv a \pmod c , x\equiv b \pmod d xa(modc),xb(modd)
可以设 x = k 1 c + a = k 2 d + b x = k_1c+a = k_2d+b x=k1c+a=k2d+b
k 1 c − k 2 d = b − a k_1c - k_2d = b-a k1ck2d=ba
e x g c d \rm exgcd exgcd求出 k 1 k_1 k1的一个特解 k 0 k_0 k0(记住中间要判断无解)
则可以合并这两个同余方程为 x ≡ k 0 c + a ( m o d c d gcd ⁡ ( c , d ) ) x \equiv k_0c+a \pmod {\frac {cd}{\gcd (c,d)}} xk0c+a(modgcd(c,d)cd)

然后就可以解决此题了。
A C   C o d e \mathrm AC \ Code AC Code

#include<bits/stdc++.h>
#define LL long long
#define maxn 100005
using namespace std;

int n,m;
LL a[maxn],p[maxn],b[maxn],c[maxn];
LL mul(LL a,LL b,LL p){
	LL r = a * b - (LL)((long double)a / p * b) * p;
	if(r < 0) return r + p;
	if(r >= p) return r - p;
	return r;
}
multiset<LL>st;
void exgcd(LL a,LL b,LL &x,LL &y,LL &gcd){
	if(!b) gcd=a,x=1,y=0;
	else exgcd(b,a%b,y,x,gcd),
		y -= (a/b) * x;
}

int main(){
	int T;
	scanf("%d",&T);
	for(;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",&b[i]);
		st.clear();
		for(int i=1;i<=m;i++) scanf("%lld",&c[i]),st.insert(c[i]);
		LL lim = 1 , x = 0 , mod = 1;
		for(int i=1;i<=n;i++){
			set<LL>::iterator it = st.upper_bound(a[i]);
			if(it != st.begin()) it--;
			LL v = *it , sx , sy , gx;
			st.erase(it);
			exgcd(v,p[i],sx,sy,gx);
			if(a[i] % gx){
				mod = -1;
				break;
			}
			sx = mul(sx,a[i]/gx,p[i]/gx);
			gx = p[i] / gx;
			lim = max(lim , (a[i] + v - 1) / v);
			
			LL k1,k2,gd;
			exgcd(gx,mod,k1,k2,gd);
			if((x-sx) % gd){
				mod= -1;
				break;
			}
			mod = mod * (gx / gd);
			k1 = mul(k1 , (x-sx) / gd , mod);
			x = (mul(k1 , gx , mod) + sx) % mod;
			st.insert(b[i]);
		}
		if(mod == -1){
			puts("-1");
			continue;
		}
		LL K = (lim - x + mod - 1) / mod;
		x = max(x , K * mod + x);
		printf("%lld\n",x);
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值