洛谷P3868 [TJOI2009]猜数字(中国剩余定理,扩展欧几里德)

洛谷题目传送门

90分WA第二个点的看过来!

简要介绍一下中国剩余定理

中国剩余定理,就是用来求解这样的问题:

假定以下出现数都是自然数,对于一个线性同余方程组(其中\(\forall i,j\in[1,k],i\neq j,b_i\)\(b_j\)互质)

\(\begin{cases}n\equiv a_1(\mod b_1)\\n\equiv a_2(\mod b_2)\\......\\n\equiv a_k(\mod b_k)\end{cases}\)

\(lcm=\prod_{i=1}^kb_i\),那么此方程组在模\(lcm\)意义下有且仅有一个解\(\sum_{i=1}^ka_ix_i\frac{lcm}{b_i}\),其中\(\frac{lcm}{b_i}x_i\equiv1(\mod b_i)\)

证明思路什么的看别的大佬的吧

那么就可以直接用扩欧或欧拉定理求\(x_i\),轻松地得到结果。

由于直接乘的时候会爆longlong,所以要写快速乘,及时取模

可是还WA第二个点是怎么回事?

原来题目里说了\(a_i\)可能为负数!真是用(sang)心良(bing)苦(kuang)啊!

既然是模\(b_i\)意义下的,可以很快把它转成正数。

#include<cstdio>
#define LL long long
#define in(x) scanf("%lld",x)
#define add(a,b) a=(a+b)%s
LL k,a[11],b[11],i,x,y,t,s=1,ans=0;
void exgcd(LL a,LL b){//扩欧
    if(!b){x=1;y=0;return;}
    exgcd(b,a%b);
    t=x;x=y;y=t-a/b*y;
}
LL mul(LL a,LL b){//快速乘
    LL r=0;
    while(b){
        if(b&1)add(r,a);
        add(a,a);
        b>>=1;
    }
    return r;
}
int main(){
    in(&k);
    for(i=1;i<=k;++i)in(a+i);
    for(i=1;i<=k;++i)in(b+i),s*=b[i];//求lcm
    for(i=1;i<=k;++i){
        exgcd(s/b[i],b[i]);
        x=(x%b[i]+b[i])%b[i];//求xi最小非负整数解
        add(ans,mul(s/b[i]*x,(a[i]%b[i]+b[i])%b[i]));//别忘处理ai
    }
    printf("%lld\n",ans);
    return 0;
}

以上的main函数里,蒟蒻把\(x_i,a_i\)都化成了最小非负整数。实际上,同余的性质很好,用不着化来化去的。最精简的写法是这样

    for(i=1;i<=k;++i){
        exgcd(s/b[i],b[i]);
        add(ans,mul(s/b[i]*x,a[i]%b[i]+b[i]));
    }

转载于:https://www.cnblogs.com/flashhu/p/9150786.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值