中国剩余定理

模板:
x = ∑ i = 1 n p i ∗ N q i ∗ [ ( N q i ) − 1 ] q i \sum_{i=1}^n p_i * \frac N{q_i} * [(\frac N{q_i})^{-1}]_{q_i} i=1npiqiN[(qiN)1]qi

int p[maxn],q[maxn],n;
int exgcd(int a,int b,int &x,int &y){
    if(b==0){
        x = 1;
        y = 0;
        return a;
    }
    int g = exgcd(b,a%b,y,x);
    y -= a/b * x;
    return g;
}
int CRT()
{
    int x,y,N = 1;
    int ans = 0;
    for(int i=1;i<=n;i++)   N *= q[i];
    for(int i=1;i<=n;i++){
        int tmp = N / q[i];
        int g = exgcd(tmp,q[i],x,y);	
        int tp = q[i]/g;
        x = (x%tp + tp)%tp;	//最小非负模逆
        ans = (ans + p[i]*tmp*x)%N;
    }
    return ans%N;
}

模板题:https://www.luogu.org/problem/P3868
题目描述
现有两组数字,每组k个,第一组中的数字分别为:a1,a2,…,ak表示,第二组中的数字分别用b1,b2,…,bk表示。其中第二组中的数字是两两互素的。求最小的非负整数n,满足对于任意的i,n - ai能被bi整除。

输入格式
输入数据的第一行是一个整数k,(1 ≤ k ≤ 10)。接下来有两行,第一行是:a1,a2,…,ak,第二行是b1,b2,…,bk

输出格式
输出所求的整数n。

输入
3
1 2 3
2 3 5
输出
23
说明/提示
所有数据中,第一组数字的绝对值不超过109(可能为负数),第二组数字均为不超过6000的正整数,且第二组里所有数的乘积不超过1018

最直接得板子题,唯一注意的式pi可能是负数,预处理 p[i] = (p[i]%q[i]+q[i])%q[i]);

#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<ctime>
#define ll long long
#define ld long double
#define ull unsigned long long
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 10100;
ll p[12],q[12];
int n;
ll mul(ll x,ll y,ll z){
    ll sm = (ld)x/z*y;
    return ((ull)x*y-(ull)sm*z+z)%z;
}
ll exgcd(ll a,ll b,ll &x,ll &y){
    if(b==0){
        x = 1;
        y = 0;
        return a;
    }
    ll g = exgcd(b,a%b,y,x);
    y -= a/b * x;
    return g;
}
void CRT()
{
    ll N = 1,x,y,ans = 0;
    for(int i=1;i<=n;i++)   N *= q[i];
    for(int i=1;i<=n;i++){
        ll tmp = N / q[i];
        ll g = exgcd(tmp,q[i],x,y);
        ll tp = q[i]/g;
        x = (x%tp + tp)%tp;
        ans = (ans+mul(mul(p[i],tmp,N),x,N))%N;
    }
    printf("%lld\n",ans%N);
}
int main(void)
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%lld",&p[i]);
    for(int i=1;i<=n;i++){
        scanf("%lld",&q[i]);
        p[i] = (p[i]%q[i]+q[i])%q[i];	//**
    }
    CRT();
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逃夭丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值