题解 CF338D 【GCD Table】

Siyuan

2019-02-08 11:05:32

Solution

[$$\Large\texttt{My Blog}$$](https://hydingsy.github.io/articles/problem-Codeforces-338D-GCD-Table/) --- ## Description > 题目链接:[Codeforces 338D](https://codeforces.com/contest/338/problem/D) 有一张 $n\times m$ 的表格,第 $i$ 行第 $j$ 列的元素是 $\gcd(i,j)$。给定一个长度为 $k$ 的序列 $a_i$,询问是否存在 $x,y$,满足 $\forall i,1\le i\le k,\gcd(x,y+i-1)=a_i$(即这个序列在表格的某一行出现过)。 数据范围:$1\le n,m\le 10^{12}$,$1\le k\le 10^4$,$1\le a_i\le 10^{12}$ ------ ## Solution 由于我们要保证 $\gcd(x,y)=a_i$,显然 $\operatorname{lcm}(a_1,a_2,\dots,a_k)\mid x$。 我们尝试证明:如果有解,那么 $x$ 的值可以为 $\operatorname{lcm}(a_1,a_2,\dots,a_k)$。 如果有解,且 $x=K\cdot\operatorname{lcm}(a_1,a_2,\dots,a_k)$,那么意味着 $\gcd(K,y)=0$,这样一来我们给 $y$ 平白无故地增加了限制。因此如果 $x=K\cdot\operatorname{lcm}(a_1,a_2,\dots,a_k)$ 时有解,那么在 $x=\operatorname{lcm}(a_1,a_2,\dots,a_k)$ 时一定也有解。 在确定了 $x$ 的值之后,还需要满足 $a_i\mid y+i-1$,即满足下列同余方程: $$\begin{cases}y=0\pmod{a_1} \\y=-1\pmod{a_2} \\\vdots \\y=-k+1\pmod{a_k}\end{cases}$$ 这个方程显然可以通过**扩展中国剩余定理**来求解 $y$ 即可。 但是我们发现,求出 $y$ 之后的解 $(x,y)$ 不一定就是合法的,这是为什么呢? 其实我们通过这样的思路,推导出解**只满足必要性**,而**不满足充分性**。因此我们还需要将 $(x,y)$ 代入 $\gcd(x,y+i-1)=a_i(1\le i\le k)$ 验证。 **时间复杂度**:$O(k\log a_i)$ ------ ## Code ```cpp #include <cstdio> typedef long long LL; const int N=1e4+5; int k; LL x,y,m[N],r[N]; LL mul(LL x,LL p,LL mod) { if(p<0) x=-x,p=-p; LL ret=0; for(;p;p>>=1,x=(x+x)%mod) if(p&1) ret=(ret+x)%mod; return ret; } LL exgcd(LL a,LL b,LL &x,LL &y) { if(!b) {x=1,y=0;return a;} LL d=exgcd(b,a%b,y,x); y-=a/b*x; return d; } LL gcd(LL x,LL y) { return y?gcd(y,x%y):x; } LL lcm(LL x,LL y) { return x/gcd(x,y)*y; } LL China(int n) { LL M=m[1],R=r[1]; for(int i=2;i<=n;++i) { LL a=M,b=m[i],c=r[i]-R,x,y,d=exgcd(a,b,x,y); if(c%d) return -1; a/=d,b/=d,c/=d,x=(mul(x,c,b)+b)%b; R+=x*M,M*=m[i]/d,R=(R+M)%M; } R=(R+M-1)%M+1; if(R<1||R+k-1>y) return -1; return R; } int main() { scanf("%lld%lld%d",&x,&y,&k); LL xx=1; for(int i=1;i<=k;++i) { scanf("%lld",&m[i]); if((xx=lcm(xx,m[i]))>x) return puts("NO"),0; r[i]=((m[i]-i+1)%m[i]+m[i])%m[i]; } LL yy=China(k); if(yy==-1) return puts("NO"),0; for(int i=1;i<=k;++i) { if(gcd(xx,yy+i-1)!=m[i]) return puts("NO"),0; } return puts("YES"),0; } ```