【題解】CF338D GCD Table

CF338D GCD Table

\[\text{Solution} \]

我們依次來考慮 \(i,j\) 需要滿足的條件。

首先,既然滿足所有 \(1\leq l\leq k\) 滿足 \(\gcd(i,j+l-1)=a_l,\) 那麼可以合理導出 \(\forall a_l,a_l|i\to \text{lcm}(a)_{i=1}^{k}|i\)

也就是 \(i\) 應當是 \(\text{lcm}\) 的倍數形式。

那麼,設最小公倍數爲 \(M,\)\(i=k\times M,\) 有一個推論:\(k=1\) 最優。

證明:若 \(k\not =1,\)\(\gcd(i,j+l-1)=\gcd(k\times M,j+l-1)\)

我們發現,\(\gcd\) 的式子裏面平白無故多了一個 \(k.\) 同時,假定 \(j\) 滿足條件,那麼當 \(i\) 取最小的時候,不合法當且僅當 \(\gcd(i,j+l-1)>a_l,\) 而將 \(i\to M\times k\) 並不會將答案變小,所以並不優。

那麼 \(i\) 的構造就顯然了,直接取最小公倍數即可。

考慮 \(j\) 如何構造。容易發現, \(j\) 需要滿足的性質應該是 \(a_l|j+l-1,\) 也就是 \(j+l-1\equiv 0(\bmod a_l)\to j\equiv 1-l(\bmod a_l)\)

於是這就是一堆同餘方程組了,但我們還會發現 \(a_l\) 之間並不互質。所以我們需要 ExCRT 來解決。

考慮無解,我們發現,解決方程組只是必要條件,直接帶入檢驗即可。

一些細節:

  • 注意解擴展中國剩餘定理的時候,方程的解是相對於前 \(k-1\) 個方程的,所以對應解出來的數不能隨便模別的數

  • 注意到擴展歐幾里得的通解形如 \(x=x_0+k\times \frac{c}{\gcd(a,b)}\) 如果我們要讓它最小就讓它取正數後對 \(\frac{c}{\gcd(a,b)}\) 取模即可

  • 這題數據範圍大,會爆,需要龜速乘

  • 當解要大的時候就算無解了,因爲超出了表格的範圍

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e4+10;
int n,m,k,a[N],ans1=1,ans2;
inline int gcd(int x,int y){return !y?x:gcd(y,x%y);}
void Exgcd(int a,int b,int &x,int &y){
	if(!b){
		x=1;y=0;
		return;
	}
	Exgcd(b,a%b,x,y);
	int t=x;x=y;y=t-a/b*y;
}
inline int LCM(int x,int y){return x/gcd(x,y)*y;}
inline int Min(int x,int y){return x>y?y:x;}
inline int Max(int x,int y){return x<y?y:x;}
int Mul(int x,int y,int p){
	if(y<0)x=-x,y=-y;
	int s=0;
	while(y){
		if(y&1)s=(s+x)%p;
		x=(x+x)%p;y>>=1;
	}
	return s;
}
void solve(int *A,int *b,int len){
	int M=b[1];
	int xx=0;
	for(int i=2;i<=k;++i){
		int r=A[i]-xx;
		int t,y;
		int d=gcd(M,b[i]);
		if(r%d!=0){
			puts("NO");
			exit(0);
		}
		Exgcd(M,b[i],t,y);
		int nM=LCM(M,b[i]);
		t=Mul(t,r/d,b[i]/d);
		xx=(xx%nM+t%nM*M%nM)%nM;
		if(M>m){
			puts("NO");
			exit(0);
		}
		M=nM;
		xx%=M;xx+=M;xx%=M;
	}
	xx%=M;xx+=M;xx%=M;
	if(xx==0)xx=M;
	if(xx>m){
		puts("NO");
		exit(0);
	}
	ans2=xx;
	for(int i=1;i<=k;++i){
		if(gcd(ans1,ans2+i-1)!=a[i]){
			puts("NO");
			exit(0);
		}
	}
	puts("YES");
}
int num[N];
signed main(){
// 	freopen("in.txt","r",stdin);
	cin>>n>>m>>k;
	for(int i=1;i<=k;++i)cin>>a[i];
	for(int i=1;i<=k;++i){
		ans1=LCM(ans1,a[i]);
		if(ans1>n){
			puts("NO");
			return 0;
		}
	}
	for(int i=1;i<=k;++i)num[i]=(((a[i]-i+1+a[i])%a[i])+a[i])%a[i];
	solve(num,a,k);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章