【模板】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]
本文来自博客园,作者:caijianhong,转载请注明原文链接:https://www.cnblogs.com/caijianhong/p/template-crt.html