链接
https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4172
题解
通过做这道题,我对扩展欧几里得求解
a
x
+
b
y
=
c
ax+by=c
ax+by=c的理解更深入了
因为它已经给出了一个根
r
r
r
假设另一个根是
k
k
k
则有
r
2
−
k
2
≡
0
(
m
o
d
n
)
r^2-k^2\equiv 0\ (mod\ n)
r2−k2≡0 (mod n)
即
(
r
−
k
)
(
r
+
k
)
≡
0
(
m
o
d
n
)
(r-k)(r+k)\equiv0(mod\ n)
(r−k)(r+k)≡0(mod n)
即
(
r
−
k
)
(
r
+
k
)
=
t
n
(r-k)(r+k)=tn
(r−k)(r+k)=tn,其中
t
t
t是整数
显然
(
r
−
k
)
+
(
r
+
k
)
=
2
r
(r-k)+(r+k)=2r
(r−k)+(r+k)=2r这个性质很好,肯定能够帮助我们做题
于是想到能不能转化成
a
x
+
b
y
=
2
r
ax+by=2r
ax+by=2r解不定方程
如果只是令
a
=
b
=
1
a=b=1
a=b=1,那这个范围也太大了,枚举的复杂度有点高
假设
(
r
−
k
)
=
a
x
,
(
r
+
k
)
=
b
y
(r-k)=ax,(r+k)=by
(r−k)=ax,(r+k)=by,那么
a
x
×
b
y
=
t
n
ax\times by=tn
ax×by=tn
等式的右边是一个任意整数乘以
n
n
n,那么左边肯定能提出因子
n
n
n
这一点可以缩小我们的枚举范围
我令
a
b
=
n
ab=n
ab=n
那么只需要在
O
(
n
)
O(\sqrt n)
O(n)时间内枚举对应的数对
(
a
,
b
)
(a,b)
(a,b)
再解一个不定方程就行了
代码
//扩展欧几里德、数学推导
#include <bits/stdc++.h>
#define ll long long
#define maxn 1000010
using namespace std;
ll lis[maxn], N, x, r;
void exgcd(ll a, ll b, ll &x, ll &y)
{
ll xx, yy;
if(!b){x=1, y=0;return;}
exgcd(b,a%b,xx,yy);
x=yy, y=xx-a/b*yy;
}
ll gcd(ll a, ll b){return !b?a:gcd(b,a%b);}
bool solve_equation(ll a, ll b, ll c, ll &x, ll &y)
{
ll g=gcd(a,b);
if(c%g)return false;
exgcd(a,b,x,y);
x*=c/g, y*=c/g;
return true;
}
ll read(ll x=0)
{
ll c, f=1;
for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-f;
for(;isdigit(c);c=getchar())x=x*10+c-0x30;
return f*x;
}
void solve()
{
ll A, B, tmp, x, y, K, i;
*lis=0;
for(A=1;A*A<=N;A++)
if(N%A==0)
{
lable:
B=N/A;
if(!solve_equation(A,B,2*r,x,y))continue;
tmp=A/gcd(A,B);
y%=tmp;
for(;B*y-r<N+tmp;y+=tmp)
{
K=B*y-r;
K=(K%N+N)%N;
lis[++*lis]=K;
}
if(A*A<N){A=N/A;goto lable;}
else A=N/A;
}
sort(lis+1,lis+*lis+1);
for(i=1;i<=*lis;i++)if(lis[i]!=lis[i-1])printf(" %lld",lis[i]);
putchar(0xa);
}
int main()
{
ll kase=0;
while(N=(read(),read()), r=read())printf("Case %lld:",++kase), solve();
return 0;
}