POJ2891 中国剩余定理推论+归纳思想
题面
POJ2891题面
思路
归纳思想
这种思想真的不知道用在多少算法上面了(递推(还记得汉诺塔怎么做的吗),递归,dp,fjord……),虽然有时很难理解,但是用这种方法思考问题的方式真的应该成为每一个ACMer思考问题的方式之一,解决离散问题的首选……
对于方程
{
x
≡
a
1
(
m
o
d
m
1
)
x
≡
a
2
(
m
o
d
m
2
)
⋮
x
≡
a
n
(
m
o
d
m
n
)
(1)
\begin{cases} x \equiv a_1(mod\space m_1)\\ x \equiv a_2(mod\space m_2)\\ \vdots \\ \tag{1} x \equiv a_n(mod\space m_n) \end{cases}
⎩⎪⎪⎪⎪⎨⎪⎪⎪⎪⎧x≡a1(mod m1)x≡a2(mod m2)⋮x≡an(mod mn)(1)
假设我们已经知道了前k个方程的一个解
x
x
x,那么这时还是取
m
=
l
c
m
(
m
1
,
m
2
,
⋯
,
m
k
)
(2)
m = lcm(m_1,m_2,\cdots,m_k) \tag{2}
m=lcm(m1,m2,⋯,mk)(2)
那么对于(1)中的前k个的mod,m肯定取模都是0的(或者说
m
i
∣
m
,
i
∈
[
1
,
k
]
m_i\mid m,i\in[1,k]
mi∣m,i∈[1,k]),所以通解肯定是
x
+
p
∗
m
(
p
∈
Z
)
x+p*m(p\in Z)
x+p∗m(p∈Z),那么现在想在其中找一个解(就是找一个p)满足第k+1个方程
x
+
p
∗
m
≡
a
k
+
1
(
m
o
d
m
k
+
1
)
x+p*m \equiv a_{k+1}(mod\space m_{k+1})
x+p∗m≡ak+1(mod mk+1),即
p
∗
m
≡
a
k
+
1
−
x
(
m
o
d
m
k
+
1
)
p*m \equiv a_{k+1}-x(mod\space m_{k+1})
p∗m≡ak+1−x(mod mk+1),找出
a
k
+
1
−
x
(
m
o
d
m
k
+
1
)
a_{k+1}-x(mod\space m_{k+1})
ak+1−x(mod mk+1)的最小正整数
q
q
q,然后转化对应的Eulicd方程
p
∗
m
+
y
∗
m
k
+
1
=
q
p*m+y*m_{k+1}=q
p∗m+y∗mk+1=q,还是用exgcd,求出
g
c
d
(
m
,
m
k
+
1
)
gcd(m,m_{k+1})
gcd(m,mk+1)判断能不能整除q(否则无解)(是不是和刚刚求得gcd很像),有解求出p,这时就得到了前k+1个方程的一个解
x
+
p
∗
m
x+p*m
x+p∗m,归纳完毕
为了最后导出最小整数解,要把结果对
l
c
m
(
m
1
,
m
2
,
⋯
,
m
n
)
lcm(m_1,m_2,\cdots,m_n)
lcm(m1,m2,⋯,mn)取模
这个过程实际上就是递归生成法一中的ans的过程
注意事项
1)这里涉及的变量很多,最好把演算时的变量和程序中的变量同名,不然很容易乱
2)一定要注意int和long long 的上限,像这种取模运算题,不要用*=,+=这类运算符
复习反馈
1)没有理解
a
x
+
b
y
=
c
ax+by=c
ax+by=c的本质,用exgcd解出x特解
x
0
x_0
x0后,此时是
a
x
+
b
y
=
g
c
d
(
a
,
b
)
ax+by=gcd(a,b)
ax+by=gcd(a,b)的方程的解,显然
x
=
x
0
+
k
∗
b
(
k
∈
Z
)
x = x_0+k*b(k\in Z)
x=x0+k∗b(k∈Z),但是对于原方程的解即使是放大倍数仍然是以b为循环节,也就是说所得的解
a
n
s
=
x
0
∗
c
/
g
c
d
(
a
,
b
)
(
m
o
d
b
)
ans = x_0*c/gcd(a,b)(mod\space b)
ans=x0∗c/gcd(a,b)(mod b)
2)还是没有理解这里的归纳的本质,虽然对于前k个方程求完解之后可以对x取模求最小,但是用来求和的变量只有在最后一步才能取模,否则就失去了x+t*lcm的解的形式,下一步的t也就没法解了
代码
#include <iostream>
using namespace std;
typedef long long ll;
ll x,y;
ll exgcd(ll a,ll b,ll &x,ll &y)
{
if(!b){
x=1;y=0;return a;
}
else
{
ll d = exgcd(b,a%b,x,y);
ll z = x;x = y; y = z - (a/b)*y;
return d;
}
}
int main()
{
int kase;
cin >>kase;
ll a,b,m,ans;bool flag = true;
for(int i = 0;i <kase;i++)
{
cin >> a >> b;//a mod // b remindar
if(i == 0) m = a,ans = b;
else
{
ll ak = (b-ans%a+a)%a;
ll tmp = exgcd(m,a,x,y);
x = ((x%a)+a)%a;
if(ak%tmp) {flag = false;break;}
x=x*(ak/tmp)%a;
ans += x*m;
//m *= a;m/=tmp;
m = m /tmp*a;
ans = (ans%m+m)%m;
}
}
if(!flag) ans = -1;
cout <<ans <<endl;
return 0;
}