【训练题23:中国剩余定理】猜数字 | P3868 [TJOI2009]

猜数字 | P3868 [TJOI2009]

前置知识

拓欧求逆元:见我的这篇
快速乘

难度

提 高 + / 省 选 − \color{cyan}提高+/省选- +/
C R T CRT CRT 的模板题,学到了许多技巧

题意

给定两个数组 a 1 ⋯ a k a_1 \cdots a_k a1ak b 1 ⋯ b k b_1 \cdots b_k b1bk
让你求出一个最小的 n n n ,使得 ∀ i ∈ [ 1 , k ] → b i ∣ ( n − a i ) \forall i\in[1,k]\rightarrow b_i \big|(n-a_i) i[1,k]bi(nai)

数据范围

b i b_i bi 两两互素
1 ≤ k ≤ 10 1\le k\le 10 1k10
∣ a i ∣ < 1 0 9 |a_i|<10^9 ai<109
1 ≤ b i ≤ 6 × 1 0 3 1\le b_i\le 6\times10^3 1bi6×103
∏ b i < 1 0 18 \prod b_i<10^{18} bi<1018

思路

  • 化简所满足的式子,就是求 n ≡ a i ( m o d b i ) n\equiv a_i\pmod{b_i} nai(modbi)
  • 这个就是中国剩余定理CRT了
  • M = ∏ b i M=\prod b_i M=bi,令 M i = M b i M_i=\frac{M}{b_i} Mi=biM,令 M i − 1 M_i^{-1} Mi1为模 b i b_i bi 意义下 M i M_i Mi 的逆元
  • 其中必须满足 b i b_i bi 两两互素。
  • 则最终答案为 ( ∑ i   a i M i M i − 1 ) % M \Big(\sum_i\ a_iM_iM_i^{-1}\Big)\%M (i aiMiMi1)%M
  • 证明可以看MashiroSky:中国剩余定理学习笔记,很详细很好。
  • 注意点1:里面 a i a_i ai 可能是负数,首先转化成取模意义下的正数。
  • 注意点2:中间 a i M i M i − 1 a_iM_iM_i^{-1} aiMiMi1 有的数很大,模可能达到 1 0 18 10^{18} 1018,所以直接乘会爆long long,需要使用到快速乘
  • 注意点3:中间 b i b_i bi 不一定是素数,所以求 M i − 1 M_i^{-1} Mi1 需要使用拓欧求逆元。

核心代码

/*
 _            __   __          _          _
| |           \ \ / /         | |        (_)
| |__  _   _   \ V /__ _ _ __ | |     ___ _
| '_ \| | | |   \ // _` | '_ \| |    / _ \ |
| |_) | |_| |   | | (_| | | | | |___|  __/ |
|_.__/ \__, |   \_/\__,_|_| |_\_____/\___|_|
        __/ |
       |___/
*/
ll aa[20],bb[20];
ll qmul(ll a,ll b,ll mod){
    
    		/// 快速乘,可以和快速幂对比一下
    ll ans = 0;
    a = (a%mod+mod)%mod;
    if(b>a)swap(a,b);
    while(b){
    
    
        if(b&1){
    
    
            ans = (ans + a) % mod;
        }
        a = (a+a)%mod;
        b>>=1;
    }
    return ans;
}
ll ex_gcd(ll a,ll b,ll &x,ll &y){
    
    	/// 拓欧代码
    if(b==0){
    
    
        x = 1;
        y = 0;
        return a;
    }
    ll ans = ex_gcd(b,a%b,x,y);
    ll tmp = x;
    x = y;
    y = tmp - a/b*y;
    return ans;
}
ll inv2(ll a,ll mod){
    
    				/// 拓欧求逆元
    ll x,y;
    ll g = ex_gcd(a,mod,x,y);
    if(g!=1)return -1;
    return (x%mod+mod)%mod;
}
int main()
{
    
    
    int n;
    cin >> n;
    ll M = 1LL;
    for(int i = 1;i <= n;++i)cin >> aa[i];
    for(int i = 1;i <= n;++i)cin >> bb[i],M*=bb[i];
    ll ans = 0;
    for(int i = 1;i <= n;++i){
    
    
        ans = (ans + qmul(qmul(aa[i],M/bb[i],M),inv2(M/bb[i],bb[i]),M))%M;
        ans = (ans + M) % M;
    }
    cout << ans;

    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_45775438/article/details/111739613