D. GCD Table (338D)

D. GCD Table (338D)

tag: exCRT exGCD

题目链接

题意:

给你一个'n*m'的矩阵,格子[i,j]的值G[i][j] = gcd(i,j)
再给你一串长度为k的序列a
问你能否在矩阵的某一行中,找到连续的k个格子,满足下列条件

  • 设第一个格子位置为[x,y]
  • 使 g[x][y] = a[1], g[x][y+1] = a[2] ... g[x][y+k-1] = a[k]
    输出“YES"或"NO"即可

做法:

本篇题解需要提前学会exgcd和excrt,我也是初学,推荐如下学习资料
博客
视频

首先我们来分析一下x

// gcd(x,y+i-1) = a[i],
// a[i] 都是x的因子,所以x一定是a[i]公倍数的倍数
// x = lcm(a[1],a[2]...a[k])*N 

接着分析一下y

// y%a[1] = 0, (y+1)%a[2] = 0, (y+2)%a[3] = 0 ...
// (y+1)%a[2] = 0  ->  y%a[2] = a[2]-1
// y%a[2] = a[2]-1
// y%a[3] = a[3]-1
// ...
// y%a[k] = a[k]-k+1 注意 a[k]-k+1不一定落在0到a[k]-1之间,记得取模
// 由此我们可以得到y的同余方程组,利用excrt求特解即可
// 模数mo[i] = a[i], 余数re[i] = (mo[k]-k+1+mo[k])%mo[k]

按照上面的思路我们就可以求出答案了,但再唠几句
我们通过excrt可以求得y的特解ans,y的通解可以表示为
LCM = lcm(mo[1],mo[2],..mo[k])
y%LCM = ans%LCM
同时我们知道x是LCM的倍数,不妨让x = LCM , 那么 y%x = ans%x
根据同余性质推得 gcd(ans,x) = gcd(y,x)

代码:

#define fst std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout << std::fixed << std::setprecision(20)
#define le "\n"
#define ll long long 
#include <bits/stdc++.h>
using namespace std;
const int N=2e5+50;
ll mo[N],re[N]; //模数、余数
ll n,m,k;
inline ll gcd(ll n,ll m){
    return (m ? gcd(m,n%m) : n); 
}
inline ll lcm(ll n,ll m){
    return  n/gcd(n,m)*m;
}
inline ll qMul(ll n,ll m,ll mod){//龟速乘防炸
    if(m<0) n = -n,m = -m; 
    ll res = 0;
    while(m){
        if(m&1) res = (res+n)%mod;
        n = (n+n)%mod;
        m>>=1;
    }
    return res%mod;
}

ll exgcd(ll a,ll b,ll &x,ll &y){
    if(b==0){
        x = 1;
        y = 0;
        return a;
    }
    ll q = exgcd(b,a%b,y,x); //交换x,y
    y -= a/b*x;
    return q;
}


int main() {
    cin>>n>>m>>k;
    ll Lcm = 1;
    for(int i=1;i<=k;i++){
        cin>>mo[i];
        Lcm = lcm(mo[i],Lcm);
        if(Lcm>n){
            cout<<"NO"<<le;
            return 0;
        }
        re[i] = ((mo[i]-i+1)+mo[i])%mo[i];
    }

    ll ans = re[1],M = mo[1]; // ans式子特解,M为模数的lcm
    ll x,y;
    for(int i=2;i<=k;i++){
        ll mi = mo[i],res = ((re[i]-ans)%mi+mi)%mi;
        ll gcd = exgcd(M,mi,x,y); 
        if(res%gcd!=0){
            cout<<"NO"<<le;
            return 0;
        }
        x = qMul(x,res/gcd,mi);
        ans += x*M;
        M = mi/gcd*M;
        ans = (ans%M+M)%M;
    }
    if(ans==0) ans = M;//这里比较细节,我们期望ans落在1~M中而不是0~M-1中,最后特判一下就行

    if(ans+k-1>m||ans<1){0
        cout<<"NO"<<le;
        return 0;
    }
    for(int i=1;i<=k;i++){
        if(gcd(Lcm,ans+i-1)!=mo[i]){
            cout<<"NO"<<le;
            return 0;
        }
    }
    cout<<"YES"<<le;
    return 0;
}

posted @ 2023-02-21 16:54  touchfishman  阅读(42)  评论(0)    收藏  举报