整理的算法模板合集: ACM模板
点我看算法全家桶系列!!!
实际上是一个全新的精炼模板整合计划
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)===a1a2ak
我们知道 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=k1c,b=k2c,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≡0modaiy≡(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)moda1y≡(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; }