「CF338D」GCD Table

problem

题意

有一张n×mn\times m的表,其中第ii行第jj列的元素是gcd(i,j)gcd(i,j)

给定一个长度为kk的序列aa,询问在表中是否存在一对(x,y)(x,y)使得i[1,k],ai=gcd(x,y+i1)\forall i\in[1,k],a_i=gcd(x,y+i-1)

Solutions

以下引自@siyuan的题解


Solution

由于我们要保证 gcd(x,y)=aigcd(x,y)=a_i,显然 lcm(a1,a2,,ak)xlcm(a_1,a_2,…,a_k)∣x

我们尝试证明:如果有解,那么xx的值可以为lcm(a1,a2,,ak)lcm(a_1,a_2,…,a_k)

如果有解,且x=K×lcm(a1,a2,,ak)x=K\times lcm(a1,a2,…,ak),那么意味着gcd(K,y)=0gcd(K,y)=0,这样一来我们给yy平白无故地增加了限制。因此如果 x=K×lcm(a1,a2,,ak)x=K\times lcm(a_1,a_2,…,a_k)时有解,那么在 x=lcm(a1,a2,,ak)x=lcm(a_1,a_2,…,a_k)时一定也有解。

在确定了xx的值之后,还需要满足aiy+i1a_i∣y+i−1,即满足下列同余方程:

{y=0(moda1)y=1(moda2)y=k+1(modak)\begin{cases}y=0\pmod {a_1}\\y=-1\pmod {a_2}\\\vdots\\y=-k+1\pmod {a_k}\end{cases}

这个方程显然可以通过扩展中国剩余定理来求解yy即可。

但是我们发现,求出yy之后的解 (x,y)(x,y)

不一定就是合法的,这是为什么呢?

其实我们通过这样的思路,推导出解只满足必要性,而不满足充分性。因此我们还需要将(x,y)(x,y)代入 gcd(x,y+i1)=ai(1ik)gcd(x,y+i−1)=ai(1≤i≤k)验证。

时间复杂度:O(klogai)O(k\log a_i)


Code

没想清楚细节WA了好几发

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;
typedef long long ll;

template <typename T>void read(T &t)
{
t=0;int f=0;char c=getchar();
while(!isdigit(c)){f|=c=='-';c=getchar();}
while(isdigit(c)){t=t*10+c-'0';c=getchar();}
if(f)t=-t;
}

const int maxk=10000+5;
ll n,m;
int K;
ll X,Y;
ll a[maxk],r[maxk];

ll gcd(ll a,ll b)
{
return b?gcd(b,a%b):a;
}

ll exgcd(ll a,ll b,ll &x,ll &y)
{
if(!b)
{
x=1,y=0;
return a;
}
ll xx,yy,re=exgcd(b,a%b,xx,yy);
x=yy;
y=xx-a/b*yy;
return re;
}

ll excrt()
{
ll M=a[1],R=r[1];
for(register int i=2;i<=K;++i)
{
ll k1,k2,g=exgcd(M,a[i],k1,k2);
if((r[i]-R)%g)return -1;
k1=(r[i]-R)/g*k1%a[i];
R+=k1*M;
M=M/g*a[i];
R%=M;
}
return (R-1+M)%M+1;
}

int main()
{
read(n),read(m),read(K);
read(a[1]),r[1]=0,X=a[1];
for(register int i=2;i<=K;++i)
{
read(a[i]),r[i]=1-i;
ll g=gcd(X,a[i]);
X=X/g*a[i];
}
Y=excrt();
if(X>n || Y+K-1>m || Y<1)
{
printf("NO");
return 0;
}
for(register int i=1;i<=K;++i)
if(gcd(X,Y+i-1)!=a[i])
{
printf("NO");
return 0;
}
printf("YES");
return 0;
}