【模板】CRT/EXCRT 中国剩余定理/扩展中国剩余定理

problem

求这个关于 \(x\) 的方程组的最小正整数解:

\[\begin{cases} x\equiv b_1 \pmod{a_1}\\ x\equiv b_2 \pmod{a_2}\\ x\equiv b_3 \pmod{a_3}\\ \cdots\\ x\equiv b_n \pmod{a_n}\\ \end{cases}\]

中国剩余定理 CRT

适用范围:\(a_1,a_2,\cdots,a_n\) 两两互质。

做法:令 \(M=\prod a_i,m_i=M/a_i\),然后 \(t_i\)\(m_i\) 在模 \(a_i\) 意义下的逆元。

答案是 \(x=\sum b_im_it_i\)。对 \(M\) 取模。

证明:当 \(x\bmod a_i\) 时,对于不是 \(i\) 的项会因为 \(a_i|m_j\) 变为 \(0\),对于 \(i\) 会因为逆元变为 \(1\)

点击查看代码

#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr,##__VA_ARGS__)
#else
#define debug(...) void(0)
#endif
typedef long long LL;
LL mod(LL x,LL m){return(x%m+m)%m;}
LL exgcd(LL a,LL b,LL c,LL& x,LL& y){
	if(!b) return x=c/a,y=0,a;
	LL res=exgcd(b,a%b,c,y,x);
	return y-=a/b*x,res;
}
LL solve(LL a,LL b,LL c){
	LL x,y,d=exgcd(a,b,c,x,y);
	return c%d==0?mod(x,b/d):-1;
}
int n,a[20],b[20];
LL M=1,ans;
int main(){
//	#ifdef LOCAL
//	 	freopen("input.in","r",stdin);
//	#endif
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d%d",&a[i],&b[i]),M*=a[i];
	for(int i=1;i<=n;i++){
		LL mi=M/a[i];
		ans+=1ll*b[i]*mi*solve(mi,a[i],1);
	}
	printf("%lld\n",ans%M?ans%M:M);

	return 0;
}

扩展中国剩余定理 EXCRT

和 CRT 一点关系没有。

考虑暴力。现在考虑的方程组的 \(a_i\)\(lcm\)\(L\),当前一个合法答案为 \(x\)

加入一个新的方程组,我们不断地使得 \(x\to x+L\),等到 \(x\equiv b_i\pmod{a_i}\) 时停止。

考虑优化,设要加 \(b\) 次,解一个方程:\(x+bL\equiv b_i\pmod{a_i}\to \boxed{a_i}c+\boxed{L}b=\boxed{b_i-x}\)

等式两边有一些系数可以对 \(a_i\) 取模。然后用 exgcd 求出它的最小非负整数解。【模板】exgcd

点击查看代码

模板题很有可能爆 long long。我不想改了。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
LL mod(LL x,LL m){return(x%m+m)%m;}
LL exgcd(LL a,LL b,LL c,LL& x,LL& y){
	if(!b) return x=c/a,y=0,a;
	LL res=exgcd(b,a%b,c,y,x);
	y-=a/b*x;
	return res;
}
LL solve(LL a,LL b,LL c,LL x=0,LL y=0){
	return c%exgcd(a,b,c,x,y)==0?mod(x,b):-1;
}
LL lcm(LL a,LL b){
	LL x,y;
	return a/exgcd(a,b,1,x,y)*b;
}
LL n,ans,tot=1;
bool update(LL a,LL b){
//	while(ans%a!=b) ans+=tot;
//	ans+kt=b(mod a)=>tk+ay=b-ans
	LL z=solve(mod(tot,a),a,mod(b-ans,a));
	if(z==-1) return 0;
	ans+=z*tot,tot=lcm(tot,a),ans=mod(ans,tot);
	return 1;
}
int mian(){
	bool flag=0;
	for(int i=1;i<=n;i++){LL a,b;
		scanf("%lld%lld",&a,&b);
		if(!update(a,b)) flag=1;
	}
	long long x=ans;
	printf("%lld\n",x);
	return 0;
}
void reset(){
	ans=0,tot=1;
}
int main(){
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	while(~scanf("%d",&n)) reset(),mian();
	return 0;
}

傻掉了,还有 haskell 代码

点击查看代码
-- Prelude.gcd
-- gcd :: Integral a => a -> a -> a
-- gcd x 0 = x
-- gcd x y = gcd y (x `mod` y)

-- Prelude.lcm
-- lcm :: Integral a => a -> a -> a
-- lcm x y = x * (y `div` (gcd x y))

exgcd :: Integral a => (a, a) -> (a, a)
exgcd (a, 0) = (1, 0)
exgcd (a, b) = let (x, y) = exgcd (b, a `mod` b) in (y, x - (a `div` b) * y)

merge :: Integral a => (a, a) -> (a, a) -> (a, a)
-- Promise: there is at least one vaild solution
--          or check it by (r1 - r2) `mod` (gcd m1 m2) == 0
merge (r1, m1) (r2, m2) = (r3 `mod` l, l) where
    d = gcd m1 m2
    l = lcm m1 m2
    (x, y) = exgcd (m1 `div` d, m2 `div` d)
    k = (r2 - r1) `div` d
    r3 = r1 + k * x * m1

reduce :: (a -> a -> a) -> [a] -> a
reduce f (x:xs) = foldl f x xs

readIntListLn :: IO [Int]
readIntListLn = fmap (map read . words) getLine

readIntegerListLn :: IO [Integer]
readIntegerListLn = fmap (map read . words) getLine

main :: IO ()
main = do
    [n] <- readIntListLn
    input <- sequence $ replicate n readIntegerListLn
    let (r, m) = reduce merge $ map (\[x, y] -> (y, x)) input
    print r

-- sequence :: (Traversable t, Monad m) => t (m a) -> m (t a)
-- replicate :: Int -> a -> [a]
posted @ 2023-03-31 18:04  caijianhong  阅读(20)  评论(0编辑  收藏  举报