【洛谷P4777】拓展中国剩余定理(Excrt)

44 篇文章 0 订阅

在这里插入图片描述
l i n k link link

分析:

前置知识: e x g c d exgcd exgcd, c r t crt crt
e x g c d exgcd exgcd用来解同余方程 c r t crt crt用来解同余方程组 其中 p i p_i pi互质
e x c r t excrt excrt也可以解同余方程组 但 p i p_i pi可以不互质

先不用管合并所有方程组 考虑合并两个
{ x ≡ A   ( m o d   P ) x ≡ a i   ( m o d   p i ) \begin{cases} x ≡A~(mod~P) \\ x≡a_i~(mod~p_i) \end{cases} {xA (mod P)xai (mod pi)
化成正常形式:
{ x = A + P k 1 x = a i + p i k 2 \begin{cases} x=A+Pk_1\\ x=a_i+p_ik_2 \end{cases} {x=A+Pk1x=ai+pik2
上下相减 得
P k 1 − p i k 2 = a i − A Pk_1-p_ik_2=a_i-A Pk1pik2=aiA
e x g c d exgcd exgcd可求出 k 1 = p i l + b k_1=p_il+b k1=pil+b ( l (l (l为任意整数 ) ) )
代入回去就可以得出 x = A + P p i l + P b x=A+Pp_il+Pb x=A+Ppil+Pb
然后这是 p i p_i pi P P P互质的时候 也就是普通 c r t crt crt

再考虑 p i p_i pi P P P不互质 先除去 g c d ( p i , P ) gcd(p_i,P) gcd(pi,P)

P g c d ( p i , P ) k 1   −   p i g c d ( p i , P ) k 2 = a i − A g c d ( p i , P ) \frac{P}{gcd(p_i,P)}k_1~-~\frac{p_i}{gcd(p_i,P)}k_2=\frac{a_i-A}{gcd(p_i,P)} gcd(pi,P)Pk1  gcd(pi,P)pik2=gcd(pi,P)aiA
同理 e x g c d exgcd exgcd得出
k 1 = p i g c d ( p i , P ) l + b k_1=\frac{p_i}{gcd(p_i,P)}l+b k1=gcd(pi,P)pil+b
然后同样代入回去
x = A + l c m ( p i , P ) l + P b x=A+lcm(p_i,P)l+Pb x=A+lcm(pi,P)l+Pb
复杂度 O ( n   l o g   n ) O(n~log~n) O(n log n)
如果防溢出 还要用龟速乘 求最小非负数解每次要处理下

CODE:

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=1e6+5;
int n;
ll a[N],p[N],x,y;
ll mul(ll a,ll k,ll Mod)
{
	ll res=0;
	while(k)
	{
		if(k&1) (res+=a)%=Mod;
		k>>=1;
		(a+=a)%=Mod;
	}
	return res;
} 
ll exgcd(ll a,ll b,ll &x,ll &y)
{
	if(b==0)
	{
		x=1;
		y=0;
		return a;
	}
	ll res=exgcd(b,a%b,x,y);
	ll k=x;
	x=y;
	y=k-a/b*y;
	return res;
}
ll excrt()
{
	ll Mod=p[1],ans=a[1];
	for(int i=2;i<=n;i++)
	{
		ll k=((a[i]-ans)%p[i]+p[i])%p[i];
		ll gcd=exgcd(Mod,p[i],x,y);
		ll qwq=p[i]/gcd;
		x=mul(x,k/gcd,qwq);
		ans+=x*Mod;
		Mod*=qwq;
		ans=(ans%Mod+Mod)%Mod;
	}
	return ans;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%lld%lld",&p[i],&a[i]);
	printf("%lld",excrt());
	
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值