题意
给定一个实数 n n ,一些硬币 个数还剩下 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(x1−y2)+b(y1−x2+[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
的最小非负整数解。
在这里进行解的存在性判断,令 。首先,若
C
C
不能整除 ,那这个方程很明显是无解的。
将
A,B,C
A
,
B
,
C
同除以
g
g
。
由上面的 函数,不难求出
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=y′−Ak
y
=
y
′
−
A
k
。
方程
Ax+By=C
A
x
+
B
y
=
C
的通解,就是
x=Cx′+Bk
x
=
C
x
′
+
B
k
,
y=Cy′−Ak
y
=
C
y
′
−
A
k
了。
那么
x
x
的最小非负整数解,即为 。
kase_insert函数
这个函数用来合并两个形如
X=Pk+A
X
=
P
k
+
A
的方程(即
X
X
模 得
A
A
)。
令这两个方程为
X=P2k2+A2
X
=
P
2
k
2
+
A
2
合并后的方程为
X=P3k3+A3
X
=
P
3
k
3
+
A
3
联立化简,最后结果为
P1k1−P2k2=A2−A1
P
1
k
1
−
P
2
k
2
=
A
2
−
A
1
使用
Exgcd
E
x
g
c
d
函数,求得
k1
k
1
的最小非负整数解
k′1
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;
}