GCD Table 【CF338D】

GCD Table

题意

已知矩阵n*m,矩阵点(x,y)的值为gcd(x,y)

各处数组a[1],a[2],a[3]\cdots a[n]是否在矩阵某一纵列连续出现

(a[1],a[2],a[3]\cdots a[k])=(p[x][y],p[x][y+1],p[x][y+2]\cdots a[x][y+k-1])

分析

对x分析

显然,若gcd(x,y+i-1)=a[i],所以x包含因数的a[1],a[2],a[3]\cdots a[n]

x为lcm(a[1],a[2],a[3]\cdots a[n])*k

对于y分析

y \mod a_{1}=0->y \mod a_{1}=0

(y+1)\mod a_{2}=0->y \mod a_{2}=-1

(y+2) \mod a_{2}=0->y \mod a_{2}=-2

\cdots\cdots

(y+k-1)\mod a_{k} =0->y\mod a_{k}=-k+1

对于上面的方程组解线性同余方程组

解法相当于解拓展中国剩余定理

充分性

满足上文条件,只是有可能产生符合条件的数

所以最终需要验证,

gcd(x,y)=a[1]

gcd(x,y+1)=a[2]

gcd(x,y+2)=a[3]

\cdots\cdots

gcd(x,y+k-1)=a[k]

坑点

在算中国剩余定理中有一处10^{12}*10^{12} \mod p,会爆long long

用以下代码防爆,x*p % mod%mod

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;
}

代码

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
#pragma warning (disable:4996)
typedef long long LL;
LL gcd(LL a, LL b) {    //最大公因数
	return b == 0 ? a : gcd(b, a % b);
}
LL lcm(LL a, LL b) {    //最小公倍数
	return a / gcd(a, b) * b;
}
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);
	y -= a / b * x;
	return q;
}
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 _x, _y, k;
LL m[10005], a[10005];
LL CRT() {        //拓展中国剩余定理
	LL m1, m2, a1, a2, x, y, c;
	m1 = m[1];
	a1 = a[1];
	bool flag = true;
	for (int i = 2; i <= k; i++) {
		m2 = m[i];
		a2 = a[i];
		c = exgcd(m1, m2, x, y);
		if ((a2 - a1) % c)
			flag = false;
		y = m2 / c;
		x = mul((a2 - a1) / c, x, y);
		x = (x % y + y) % y;
		a1 = x * m1 + a1;
		m1 = (m1 * m2) / c;
	}
	c = exgcd(1, m1, x, y);
	if (a1 % c) flag = false;
	if (!flag)
		return -1;
	x = a1 / c * x;
	y = m1 / c;
	x = (x % y + y) % y;
	return x == 0 ? y : x;
}
int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin >> _x >> _y >> k;
	LL Lcm = 1;
	for (int i = 1; i <= k; i++) {
		cin >> m[i];
		Lcm = lcm(Lcm, m[i]);
		if (Lcm > _x) {
			printf("NO\n");
			return 0;
		}
		a[i] = ((m[i] - i + 1) % m[i] + m[i]) % m[i];
	}
	LL ans = CRT();
	if (ans == -1 || ans + k - 1 > _y) {
		printf("NO\n");
		return 0;
	}
	for (int i = 1; i <= k; i++) {
		if (gcd(Lcm, ans + i - 1) != m[i]) {
			printf("NO\n");
			return 0;
		}
	}
	printf("YES\n");
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值