洛谷P3868 [TJOI2009]猜数字(中国剩余定理,扩展欧几里德)

洛谷题目传送门

90分WA第二个点的看过来!

简要介绍一下中国剩余定理

中国剩余定理,就是用来求解这样的问题:

假定以下出现数都是自然数,对于一个线性同余方程组(其中\(\forall i,j\in[1,k],i\neq j,b_i\)\(b_j\)互质)

\(\begin{cases}n\equiv a_1(\mod b_1)\\n\equiv a_2(\mod b_2)\\......\\n\equiv a_k(\mod b_k)\end{cases}\)

\(lcm=\prod_{i=1}^kb_i\),那么此方程组在模\(lcm\)意义下有且仅有一个解\(\sum_{i=1}^ka_ix_i\frac{lcm}{b_i}\),其中\(\frac{lcm}{b_i}x_i\equiv1(\mod b_i)\)

证明思路什么的看别的大佬的吧

那么就可以直接用扩欧或欧拉定理求\(x_i\),轻松地得到结果。

由于直接乘的时候会爆longlong,所以要写快速乘,及时取模

可是还WA第二个点是怎么回事?

原来题目里说了\(a_i\)可能为负数!真是用(sang)心良(bing)苦(kuang)啊!

既然是模\(b_i\)意义下的,可以很快把它转成正数。

#include<cstdio>
#define LL long long
#define in(x) scanf("%lld",x)
#define add(a,b) a=(a+b)%s
LL k,a[11],b[11],i,x,y,t,s=1,ans=0;
void exgcd(LL a,LL b){//扩欧
	if(!b){x=1;y=0;return;}
	exgcd(b,a%b);
	t=x;x=y;y=t-a/b*y;
}
LL mul(LL a,LL b){//快速乘
	LL r=0;
	while(b){
		if(b&1)add(r,a);
		add(a,a);
		b>>=1;
	}
	return r;
}
int main(){
	in(&k);
	for(i=1;i<=k;++i)in(a+i);
	for(i=1;i<=k;++i)in(b+i),s*=b[i];//求lcm
	for(i=1;i<=k;++i){
		exgcd(s/b[i],b[i]);
		x=(x%b[i]+b[i])%b[i];//求xi最小非负整数解
		add(ans,mul(s/b[i]*x,(a[i]%b[i]+b[i])%b[i]));//别忘处理ai
	}
	printf("%lld\n",ans);
	return 0;
}

以上的main函数里,蒟蒻把\(x_i,a_i\)都化成了最小非负整数。实际上,同余的性质很好,用不着化来化去的。最精简的写法是这样

	for(i=1;i<=k;++i){
		exgcd(s/b[i],b[i]);
		add(ans,mul(s/b[i]*x,a[i]%b[i]+b[i]));
	}
posted @ 2018-06-07 15:21  Flash_Hu  阅读(391)  评论(0编辑  收藏  举报