C/C++教程

CF338D GCD Table(拓展中国剩余定理,细节处理,2900分)

本文主要是介绍CF338D GCD Table(拓展中国剩余定理,细节处理,2900分),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

整理的算法模板合集: ACM模板

点我看算法全家桶系列!!!

实际上是一个全新的精炼模板整合计划


CF338D GCD Table(拓展中国剩余定理,细节处理,2900分)

Problem

有一张 n × m n\times m n×m 的表格 G G G,第 i i i 行第 j j j 列的元素是 G ( i , j ) = gcd ⁡ ( i , j ) G(i,j)=\gcd(i,j) G(i,j)=gcd(i,j) 。给定一个长度为 k k k 的序列 a i a_i ai​ ,询问是否存在 x , y x,y x,y,满足 ∀ i , 1 ≤ i ≤ k , G ( x , y + i − 1 ) = gcd ⁡ ( x , y + i − 1 ) = a i \forall i,1\le i\le k,G(x,y+i-1)=\gcd(x,y+i-1)=a_i ∀i,1≤i≤k,G(x,y+i−1)=gcd(x,y+i−1)=ai​ (即问是否有在表格范围内的解 ( x , y ) (x,y) (x,y) )。

数据范围: 1 ≤ n , m ≤ 1 0 12 , 1 ≤ k ≤ 1 0 4 , 1 ≤ a i ≤ 1 0 12 1\le n,m\le 10^{12},1\le k\le 10^4,1\le a_i\le 10^{12} 1≤n,m≤1012,1≤k≤104,1≤ai​≤1012

Solution

根据题意显然可以列出:

{ gcd ⁡ ( x , y + 1 − 1 ) = a 1 gcd ⁡ ( x , y + 2 − 1 ) = a 2 ⋯ gcd ⁡ ( x , y + k − 1 ) = a k \left\{ \begin{aligned} \gcd(x,y+1-1) & = & a_1 \\ \gcd(x,y+2-1) & = & a_2 \\ \cdots\\ \gcd(x,y+k-1)& = & a_k \end{aligned} \right. ⎩⎪⎪⎪⎪⎨⎪⎪⎪⎪⎧​gcd(x,y+1−1)gcd(x,y+2−1)⋯gcd(x,y+k−1)​===​a1​a2​ak​​

我们知道 gcd ⁡ ( a , b ) = c \gcd(a,b) =c gcd(a,b)=c,经典套路,显然有: a = k 1 c , b = k 2 c , k 1 ∤ k 2 a=k_1c,b=k_2c,k_1\nmid k_2 a=k1​c,b=k2​c,k1​∤k2​

即:
a ∣ c , b ∣ c ⇒ x ∣ a i , y + i − 1 ∣ a i a\mid c,b\mid c\Rightarrow x\mid a_i,y+i-1\mid a_i a∣c,b∣c⇒x∣ai​,y+i−1∣ai​

即:

x ≡ 0   m o d   a i y ≡ ( 1 − i )   m o d   a i \begin{aligned}&x\equiv0 \bmod a_i&\\&y\equiv(1-i)\bmod a_i\end{aligned} ​x≡0modai​y≡(1−i)modai​​

所以我们可以列出一个同余方程组:

{ y ≡ ( 1 − 1 )   m o d   a 1 y ≡ ( 1 − 2 )   m o d   a 2                ⋯ y ≡ ( 1 − k )   m o d   a k \left\{ \begin{aligned}&y\equiv(1-1)\bmod a_1&\\&y\equiv(1-2)\bmod a_2\\&\ \ \ \ \ \ \ \ \ \ \ \ \ \ \cdots&\\&y\equiv(1-k)\bmod a_k\end{aligned} \right. ⎩⎪⎪⎪⎪⎨⎪⎪⎪⎪⎧​​y≡(1−1)moda1​y≡(1−2)moda2​              ⋯y≡(1−k)modak​​​

显然 a i a_i ai​ 不一定是质数,所以我们直接使用拓展中国剩余定理求解即可,这样我们就可以求出一个合法的 y y y。

那么如何求出最小的(不容易越界)且合法的 x x x 呢,显然 x x x 可以整除所有的 a i a_i ai​,那么满足条件的最小的 x x x 显然是 lcm { a i } \text{lcm}\{ a_{i} \} lcm{ai​}。

求出 x , y x,y x,y 以后验证一下开头列出来的 gcd ⁡ \gcd gcd 是否完全相同,以及 G ( x , y + i − 1 ) G(x,y+i-1) G(x,y+i−1) 中的 ( x , y + i − 1 ) (x,y+i-1) (x,y+i−1) 是否在 G G G 函数的定义域内即可。(我们只需要判断 x , y + i − 1 , y + k − 1 x,y+i-1,y+k-1 x,y+i−1,y+k−1 是否越界即可)

Hint

  • 这里如果给定的模数均为 1 1 1 的话,直接用 excrt 板子求出来的是 0 0 0 ,此时应该是答案取任意值均可,但是这里如果答案取 0 0 0 的话 y < 1 y<1 y<1 会判负,实际上是有解的,所以这里要特判一下。

  • 龟速乘的时候,如果乘的是负数的话需要 a , b a,b a,b 同时取反,否则会死循环hhh

Code

// Problem: D. GCD Table
// Contest: Codeforces - Codeforces Round #196 (Div. 1)
// URL: https://codeforces.com/problemset/problem/338/D
// Memory Limit: 256 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define int long long
using namespace std;
using ll = long long;
const int N = 1e4 + 7;

int n, m, k, t;
int a[N], LCM;
int ai[N], bi[N];

template <typename T> inline void read(T& t) {
    int f = 0, c = getchar(); t = 0; 
    while (!isdigit(c)) f |= c == '-', c = getchar();
    while (isdigit(c)) t = t * 10 + c - 48, c = getchar();
    if (f) t = -t;
}

template <typename T> void print(T x) {
    if (x < 0) x = -x, putchar('-');
    if (x > 9) print(x / 10);
    putchar(x % 10 + 48);
} 

int gcd(int a, int b)
{
	if(b == 0) return a;
	return gcd(b, a % b);
}

int lcm(int a, int b)
{
	return a / gcd(a, b) * b;
}

int exgcd(int a, int b, int &x, int &y)
{
	if(b == 0) {
		x = 1, y = 0;
		return a;
	}
	int d = exgcd(b, a % b, x, y);
	int z = x;
	x = y, y = z - a / b * y; 
	return d;
}

int mul(int a, int b, int c)
{
	if(b < 0) a = - a, b = - b;
	int res = 0;
	while(b) {
		if(b & 1) res = (res + a) % c;
		a = (a + a) % c;
		b >>= 1;
	}
	return res;
}

ll excrt(int n)
{
	ll x, y, k;
	ll M = bi[1], ans = ai[1];
	for(int i = 2; i <= n; ++ i) {
		ll a = M, b = bi[i], c = (ai[i] - ans % b + b) % b;
		ll d = exgcd(a, b, x, y);
		ll bg = b / d;
		if(c % d != 0) return -1;
		x = mul(x, c / d, bg);
		ans += x * M;
		M *= bg;
		ans = (ans % M + M) % M;
	} 
	ans = (ans % M + M) % M;
	if(ans == 0) ans = M;//ans = 1;
	return ans;
}

signed main()
{
	LCM = 1;
	read(n), read(m), read(k);
	for(int i = 1; i <= k; ++ i) {
		read(a[i]);
		LCM = lcm(LCM, a[i]);
	}
	if(k > m) return puts("NO"), 0;
	for(int i = 1; i <= k; ++ i) {
		bi[i] = a[i];
		ai[i] = (1 - i + a[i]) % a[i];
	}

	int x = LCM, y = excrt(k); 
	if(y == -1) return puts("NO"), 0;
	for(int i = 1; i <= k; ++ i) {
		if(gcd(x, y + i - 1) != a[i]) 
			return puts("NO"), 0;
	}     

	if(y < 1 || x < 1 || y + k - 1 > m || x > n) 
		return puts("NO"), 0;
	return puts("YES"), 0;
}
这篇关于CF338D GCD Table(拓展中国剩余定理,细节处理,2900分)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!