LightOJ 1319 Monkey Tradition

求解
\[ x \equiv \left\{\begin{aligned}a_1 \mod p_1\\a_2 \mod p_2 \\... \\a_n \mod p_n\end{aligned}\right. \]
其中 \(p_i\) 为质数

若有正整数解,输出最小的,如果没有,输出 Impossible


前置芝士:

\(exgcd\) 求乘法逆元

也就是说,求 \(x\) 使得 \(ax \equiv 1 \mod b\)

相当于求 \(ax=kb+1\)

移项
\[ ax-kb=1 \]
\(y=-k\)
\[ ax+by=1 \]
这玩意明显 \(exgcd\) 能搞出来

然后正式开始

中国剩余定理 \((CRT)\)
\[ x \equiv \left\{\begin{aligned}a_1 \mod p_1\\a_2 \mod p_2 \\... \\a_n \mod p_n\end{aligned}\right. \]
\(M=p_1*p_2*...*p_n,m_i=\frac {M}{p_i}\)

因为 \(p_i\) 都是质数,所以在 \([0,M-1]\) 范围内有且只有一个解(这个自己感性理解一下就知道了)

所以我们只要出任意一个解,然后把它对 \(M\) 取膜就好了

我们假设
\[ ans_i \equiv \left\{\begin{aligned}a_i \mod p_i \\0 \mod m_i\end{aligned}\right. \]
那么这个 \(ans_i\) 他有什么特殊之处呢?它刚好满足 \(\equiv a_i \mod p_i\) 且不会“影响”到其他的 \(a_j,p_j\)

那么答案就是 $ans=\sum_{i=1}^nans_i %M $

那么我们怎么求 $ ans_i$?

首先,\(ans_i\)\(m_i\) 的倍数,所以我们设 \(ans_i=k_i*m_i\)

然后我们还要让他满足 \(\equiv a_i \mod p_i\),那么我们设 \(k_i=a_i*t_i\)

也就是说 \(ans_i=m_i*a_i*t_i\)

我们要让
\[ m_i*t_i \equiv 1 \mod p_i \]
也就是求个 \(m_i\)\(\mod p_i\) 意义下的的乘法逆元嘛,\(exgcd\) 一波

然后算出每个 \(ans_i\) 加一加取个膜,没了

时间复杂度 \(O(nlog M)\)

// This code Write By chtholly_micromaker(MicroMaker)
#include <cstdio>
#include <cctype>
#define int long long
#define reg register
using namespace std;
const int MaxN=15;
template <class t> inline void rd(t &s)
{
    s=0;
    reg char c=getchar();
    while(!isdigit(c))
        c=getchar();
    while(isdigit(c))
        s=(s<<3)+(s<<1)+(c^48),c=getchar();
    return;
}
inline int exgcd(int a,int b,int &x,int &y)
{
    if(!b)
    {
        x=1,y=0;
        return a;
    }
    reg int c=exgcd(b,a%b,y,x);
    y-=a/b*x;
    return c;
}
int n,mulsum;
int a[MaxN],p[MaxN];
inline int crt()
{
    reg int ans=0,mi,x,y;
    for(int i=1;i<=n;++i)
    {
        mi=mulsum/p[i];
        exgcd(p[i],mi,x,y);
        ans=(ans+a[i]*mi*y)%mulsum;
    }
    return (ans+mulsum)%mulsum;
}
inline void work()
{
    mulsum=1;
    rd(n);
    for(int i=1;i<=n;++i)
    {
        rd(p[i]);rd(a[i]);
        mulsum*=p[i];
    }
    printf("%lld\n",crt());
    return;
}
signed main(void)
{
    int t;rd(t);
    for(int i=1;i<=t;++i)
        printf("Case %lld: ",i),work();
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/chinesepikaync/p/12301060.html