TJOI2009 洛谷P3868 猜数字 - 中国剩余定理 - 快速乘(爆long long 的处理方法) - 很多细节。。。复习时多看看注释

不知道为什么,但是有负数就一定要先转换为正数(总之转换成正数一定不会错,因为重新得到的数在模意义下和原来相同)

比如说exgcd求逆元就要转换为最小正整数!!!

反正在同余问题的求解过程中出现了负数就转化为负数
然后因为最后乘起来会爆long long 要用快速加边加边模

还有 在能确定复杂度的前提下能开long long 就开

还有, 把模数全乘起来,是求最小公倍数,这一点在拓展中国剩余定理里也有体现(事实上拓展剩余代码中用的是最小公倍数)

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
#define debug(x) cerr << #x << "=" << x << endl;
const int MAXN = 100;
long long m[MAXN],t[MAXN],k,a[MAXN],b[MAXN];
long long ans;
typedef long long ll;
ll x, y, gcdd, ms;
void exgcd(ll a, ll b, ll &gcdd, ll &x, ll &y) {//在这里引用的值都要是全局的,并且参数名和全局变量名相同
    if(!b) {
        x = 1, y = 0;
        gcdd = a;
        return;
    }
    exgcd(b, a%b, gcdd, x, y);
    int z = x;
    x = y;
    y = z - a/b*y;
}
ll qmul(ll a, ll b, ll mod) {
    ll ans = 0;
    for(; b; b>>=1) {
        if(b & 1) {
            ans = (ans + a) % mod; 
        }
        a = (a + a) % mod;
    }
    return ans;
}
int main() {
    cin >> k;
    for(int i=1; i<=k; i++) {
        cin >> a[i];
    }
    ms = 1;
    for(int i=1; i<=k; i++) {
        cin >> b[i];
        ms *= b[i];
        a[i] = (a[i]%b[i] + b[i])%b[i];//反正在同余里面出现负数一般都要转换为正数 
    }
    for(int i=1; i<=k; i++) {
        m[i] = ms / b[i];
        exgcd(m[i], b[i], gcdd, x, y); //在纸上要想好变量名再写,纸上公式是什么变量名,程序里就写什么,避免混淆 
        t[i] = (x%b[i] + b[i]) % b[i];
    }
    for(int i=1; i<=k; i++) {
        ans = ((ans+qmul(qmul(m[i], t[i], ms), a[i], ms))%ms + ms) % ms;
    }
    cout << (ans%ms + ms) % ms;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Fantasy_World/article/details/81432323