CodeForces 338 Problem D GCD Table 拓展中国剩余定理

CF 338 problem D GCD Table

题目描述

题目链接点我
给出一张n*m的GCD表,第i行第j列的数字大小为 gcd(i,j)即i和j的最大公因数,给出一个数字序列a1,a2,。。。,ak(1 ≤ ai ≤ 1012),1<=k<=104,问该数字序列是否在GCD表的某一行中完整出现过,答案输出YES或NO
例如:

题目给出N=4,M=8,则为一张4 * 8 的 GCD 表(如下),给出数列3 1 1 2,因为它其在GCD表中出现过,答案输出YES
1 1 1 1 1 1 1 1
1 2 1 2 1 2 1 2
1 1 3 1 1 2 1 1
1 2 1 4 1 2 1 4

题目分析

首先,假如给出的数列在GCD表中x行y列开始出现。
ai = gcd(x,y+i) (1<=i<=k),则x = k * lcm(a1,…,ak),k >=1,因为ai均为x的因子。若lcm > n,则无解.
若lcm<=n,我们可以写出如下式子
t1 * a1 = y
t2 * a2 = y + 1
      . . .
tk * ak = y + (k -1)

ai = gcd(x,y+i-1)
=>         x = si * ai
&& y + i - 1 = ti * ai

将上述式子转化为同余方程的形式
y = 0      mod a1
y = -1     mod a2
. . .
y = 1 - k  mod ak
然后用拓展中国剩余定理解出y,若y>m-k+1则无解(数列未全部在n*m的GCD表中)
最后逐步验证 ai = gcd(x,y+i-1)是否全部成立

代码参考

/*
 * @Author: CHAOS_ORDER
 * @Date: 2019-08-11 14:36:48
 * @LastEditors: CHAOS_ORDER
 * @LastEditTime: 2019-09-03 20:55:21
 * @Description: GCD Table https://codeforces.com/contest/338/problem/D
 * @Status: Accepted 60ms
 */
#include <functional>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <iomanip>
#include <cstdio>
#include <string>
#include <vector>
#include <cmath>
#include <deque>
#include <queue>
#include <stack>
#include <map>
#include <set>
using namespace std;
#define sd(i) scanf("%d", &i)
#define pd(i) printf("%d", i)
#define slld(i) scanf("%lld", &i)
#define plld(i) printf("%lld", i)
#define schstr(i) scanf("%s", i)
#define pchstr(i) printf("%s", i)
#define For(i, begin, end) for (register int i = begin; i <= end; i = i + 1)
#define rFor(i, begin, end) for (register int i = begin; i >= end; i = i - 1)
#define newline printf("\n")
#define pause system("pause")
#define mod 1000000007
#define inf 0x3f3f3f3f
typedef long long ll;
const ll maxn = 1e4 + 7;
const ll maxm = 1e7 + 7;

ll a[maxn];
ll N, M;
int k;
ll exgcd(ll a, ll b, ll &x, ll &y)
{
    if (!b)
    {
        x = 1, y = 0;
        return a;
    }
    ll ans = exgcd(b, a % b, y, x);
    y -= x * (a / b);
    return ans;
}
void excrt()
{
    ll m = a[1], r = 0, x, y, g;
    For(i, 2, k)
    {
        g = exgcd(m, a[i], x, y);
        if ((1 - i - r) % g)
        {
            return pchstr("NO"), void();
        }
        ll mul = a[i] / g;
        x = x * (1 - i - r) / g;
        x = (x % mul + mul) % mul;
        r = r + m * x;
        m = m * a[i] / g;
        r = (r % m + m) % m;
    }
    if (r == 0)
        r = m;
    if (r + k - 1 > M)
        return pchstr("NO"), void();
    For(i, 1, k)
    {
        if (__gcd(m, r + i - 1) != a[i])
            return pchstr("NO"), void();
    }
    pchstr("YES");
}
bool check()
{
    ll lcm = 1;
    For(i, 1, k)
    {
        lcm = lcm / __gcd(a[i], lcm) * a[i];
        if (lcm > N)
            return false;
    } //求x = lcm(a[1],...,a[k]), N 一定大于 x ,否则答案为“NO”
    return true;
}
int main()
{
    slld(N), slld(M), sd(k);
    For(i, 1, k)
        slld(a[i]);
    if (check())
        excrt();
    else
        pchstr("NO");
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值