HDU-3579 Hello Kiki(中国剩余定理)

题意

给定一个实数 n n ,一些硬币 MiMi 个数还剩下 Ai A i (i[1,n]) ( i ∈ [ 1 , n ] ) ,求硬币的最小数目。

思路

中国剩余定理模板题,从头梳理一遍。

exgcd函数

求的是方程 ax+by=gcd(a,b) a x + b y = g c d ( a , b ) 的一组特解 (x,y) ( x , y )
首先根据 gcd g c d 函数的原理 gcd(a,b)=gcd(b,a%b) g c d ( a , b ) = g c d ( b , a % b )
那么列出两组方程并联立
ax1+by1=gcd(a,b) a x 1 + b y 1 = g c d ( a , b ) ax2+by2=gcd(b,a%b) a x 2 + b y 2 = g c d ( b , a % b )
ax1+by1=bx2+a%by2 a x 1 + b y 1 = b x 2 + a % b y 2
ax1+by1=bx2+(a[a/b]b)y2 a x 1 + b y 1 = b x 2 + ( a − [ a / b ] ∗ b ) y 2
a(x1y2)+b(y1x2+[a/b]y2)=0 a ( x 1 − y 2 ) + b ( y 1 − x 2 + [ a / b ] ∗ y 2 ) = 0
求出上式的一组特解,不难看出 x1=y2,y1=x2[a/b]y2 x 1 = y 2 , y 1 = x 2 − [ a / b ] ∗ y 2 时方程成立。
把前式代入后式,得到 x1=y2,y1=x2[a/b]x1 x 1 = y 2 , y 1 = x 2 − [ a / b ] ∗ x 1
这是最容易代码实现的方程,由于 x1,y1 x 1 , y 1 的结果来源于 x2,y2 x 2 , y 2 所以递归时把 x,y x , y 引用往下传。

Exgcd函数

求的是方程 Ax+By=C A x + B y = C x x 的最小非负整数解。
在这里进行解的存在性判断,令 g=gcd(A,B) 。首先,若 C C 不能整除 g ,那这个方程很明显是无解的。
A,B,C A , B , C 同除以 g g
由上面的 exgcd 函数,不难求出 Ax+By=gcd(A,B)=1 A x + B y = g c d ( A , B ) = 1 的特解( A,B A , B 已经互质) x,y x ′ , y ′
那么通解就是 x=x+Bk x = x ′ + B k y=yAk y = y ′ − A k
方程 Ax+By=C A x + B y = C 的通解,就是 x=Cx+Bk x = C x ′ + B k y=CyAk y = C y ′ − A k 了。
那么 x x 的最小非负整数解,即为 x=(Cx%B+B)%B

kase_insert函数

这个函数用来合并两个形如 X=Pk+A X = P k + A 的方程(即 X X P A A )。
令这两个方程为
X=P1k1+A1
X=P2k2+A2 X = P 2 k 2 + A 2
合并后的方程为
X=P3k3+A3 X = P 3 k 3 + A 3
联立化简,最后结果为
P1k1P2k2=A2A1 P 1 k 1 − P 2 k 2 = A 2 − A 1
使用 Exgcd E x g c d 函数,求得 k1 k 1 的最小非负整数解 k1 k 1 ′
A3 A 3 即为 P1k1+A1 P 1 k 1 + A 1 P3 P 3 比较明显,是 lcm(P1,P2) l c m ( P 1 , P 2 )

自此,整个模板的函数已大致介绍完毕。

代码

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define FOR(i,x,y) for(int i=(x);i<=(y);i++)
#define DOR(i,x,y) for(int i=(x);i>=(y);i--)
typedef long long LL;
using namespace std;
struct CRT
{
    LL A,P;
    LL gcd(LL a,LL b){return b?gcd(b,a%b):a;}
    void exgcd(LL a,LL b,LL &x,LL &y)
    {
        if(!b){x=1,y=0;return;}
        exgcd(b,a%b,y,x);y-=a/b*x;
        return;
    }
    LL Exgcd(LL A,LL B,LL C)
    {
        LL k1,k2,g=gcd(A,B);
        if(C%g)return -1;
        A/=g,B/=g,C/=g;
        exgcd(A,B,k1,k2);
        return (k1*C%B+B)%B;
    }
    bool kase_insert(CRT _)
    {
        LL res=Exgcd(P,_.P,_.A-A);
        if(!~res)return 0;
        A+=P*res;
        P=P/gcd(P,_.P)*_.P;
        return 1;
    }
    LL ask(){return A?A:P;}
};

int main()
{
    int T;
    scanf("%d",&T);
    FOR(Ti,1,T)
    {
        int n;
        LL M[10],A[10];
        scanf("%d",&n);
        FOR(i,1,n)scanf("%lld",&M[i]);
        FOR(i,1,n)scanf("%lld",&A[i]);
        CRT sum=(CRT){A[1],M[1]};
        bool flag=0;
        FOR(i,2,n)if(!sum.kase_insert((CRT){A[i],M[i]}))
        {
            flag=1;
            break;
        }
        printf("Case %d: %lld\n",Ti,flag?-1:sum.ask());
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值