【比赛链接】
【题解链接】
【Div.2 A】Infinity Gauntlet
【思路要点】
- 按照题意模拟即可。
- 时间复杂度\(O(NLogN)\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 100005; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } set <char> st; int main() { int n; read(n); for (int i = 1; i <= n; i++) { char s[15]; scanf("\n%s", s); st.insert(s[0]); } printf("%d\n", 6 - n); if (st.count('p') == 0) printf("Power\n"); if (st.count('g') == 0) printf("Time\n"); if (st.count('b') == 0) printf("Space\n"); if (st.count('o') == 0) printf("Soul\n"); if (st.count('r') == 0) printf("Reality\n"); if (st.count('y') == 0) printf("Mind\n"); return 0; }
【Div.2 B】High School: Become Human
【思路要点】
- 取对数比较,将eps设为\(10^{-17}\)即可。
- 时间复杂度\(O(1)\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 100005; const long double eps = 1e-17; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int main() { int x, y; read(x), read(y); long double a = y * log(x); long double b = x * log(y); if (a - b > eps) printf(">"); else if (b - a > eps) printf("<"); else printf("="); return 0; }
【Div.2 C】Three displays
【思路要点】
- 枚举中间的一个位置,在两边分别枚举前后的位置,取合法位置的代价最小值更新答案。
- 时间复杂度\(O(N^2)\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 5005; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int n, s[MAXN], c[MAXN]; int main() { read(n); for (int i = 1; i <= n; i++) read(s[i]); for (int i = 1; i <= n; i++) read(c[i]); int ans = 1e9; for (int i = 2; i <= n - 1; i++) { int pre = 1e9, suf = 1e9; for (int j = 1; j <= i - 1; j++) if (s[j] < s[i]) chkmin(pre, c[j]); for (int j = i + 1; j <= n; j++) if (s[j] > s[i]) chkmin(suf, c[j]); chkmin(ans, pre + suf + c[i]); } if (ans == 1e9) printf("-1\n"); else printf("%d\n", ans); return 0; }
【Div.2 D/Div.1 A】Fair
【思路要点】
- 对\(K\)中商品分别进行一次BFS,求得每个点到最近的该种商品距离。
- 在每个点取前\(S\)小的值作为答案。
- 使用std::sort实现取前\(S\)小的值,时间复杂度\(O(K*(N+M)+N*K*LogK)\)。
- 或者使用std::nth_element实现取前\(S\)小的值,时间复杂度\(O(K*(N+M)+N*K)\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 100005; const int MAXK = 105; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int n, m, k, s, type[MAXN]; vector <int> a[MAXN]; int dist[MAXN][MAXK]; int main() { read(n), read(m), read(k), read(s); for (int i = 1; i <= n; i++) read(type[i]); for (int i = 1; i <= m; i++) { int x, y; read(x), read(y); a[x].push_back(y); a[y].push_back(x); } memset(dist, -1, sizeof(dist)); for (int i = 1; i <= k; i++) { static int q[MAXN]; int l = 0, r = -1; for (int j = 1; j <= n; j++) if (type[j] == i) { q[++r] = j; dist[j][i] = 0; } while (l <= r) { int tmp = q[l++]; for (unsigned j = 0; j < a[tmp].size(); j++) if (dist[a[tmp][j]][i] == -1) { dist[a[tmp][j]][i] = dist[tmp][i] + 1; q[++r] = a[tmp][j]; } } } for (int i = 1; i <= n; i++) { sort(dist[i] + 1, dist[i] + k + 1); int ans = 0; for (int j = 1; j <= s; j++) ans += dist[i][j]; printf("%d ", ans); } return 0; }
【Div.2 E/Div.1 B】Petr and Permutations
【思路要点】
- 我们发现对于任意正整数\(N\),\(3N+1\)和\(7N\)奇偶性不同。
- 经过一次对换,排列的奇偶性会变化。
- 求出排列的奇偶性,根据\(3N+1\)和\(7N\)的奇偶性判断即可。
- 时间复杂度\(O(NLogN)\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 1000005; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int n, a[MAXN], bit[MAXN]; void modify(int pos) { for (int i = pos; i <= n; i += i & -i) bit[i] ^= 1; } int query(int pos) { int ans = 0; for (int i = pos; i >= 1; i -= i & -i) ans ^= bit[i]; return ans; } int main() { read(n); for (int i = 1; i <= n; i++) read(a[i]); int ans = 0; for (int i = n; i >= 1; i--) { ans ^= query(a[i]); modify(a[i]); } if (n & 1) { if (ans & 1) printf("Petr\n"); else printf("Um_nik\n"); } else { if (ans & 1) printf("Um_nik\n"); else printf("Petr\n"); } return 0; }
【Div.2 F/Div.1 C】AND Graph
【思路要点】
- 我们发现,\(x\ and\ y=0\)等价于\(y\)被\(\sim x\)数位包含。
- 考虑以下有向图:
- 若\(y\)被\(x\)数位包含,并且\(x\)与\(y\)仅有一位不同,连边\(x\Rightarrow y\)。
- 若\(x\)出现在输入中,连边\(x\Rightarrow \sim x\)。
- 在该有向图中,当且仅当\(y\)被\(x\)数位包含或\(y\)被\(\sim x\)数位包含,\(x\)可以到达\(y\)。
- \(y\)被\(x\)数位包含与\(x\)被\(y\)数位包含不可能同时成立,因此若\(x\ and\ y=0\),\(x\)和\(y\)可以相互到达。
- 求这张图的强联通分量,输入中的点所在的不同的强连通分量个数即为答案。
- 注意我们不需要将这张图的边存下来,只需要在用到时检查一下数位即可。
- 时间复杂度\(O(N*2^N)\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 4194304; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int n, m, goal; int top, Stack[MAXN]; int tot, belong[MAXN]; int timer, dfn[MAXN], low[MAXN]; bool instack[MAXN], selected[MAXN]; set <int> ans; void tarjan(int s) { Stack[++top] = s; instack[s] = true; dfn[s] = low[s] = ++timer; for (int i = 0; i < n; i++) { int tmp = 1 << i; if ((s & tmp) == 0) continue; int t = s ^ tmp; if (dfn[t] == 0) { tarjan(t); chkmin(low[s], low[t]); } else if (instack[t]) chkmin(low[s], dfn[t]); } if (selected[s]) { int t = s ^ goal; if (dfn[t] == 0) { tarjan(t); chkmin(low[s], low[t]); } else if (instack[t]) chkmin(low[s], dfn[t]); } if (low[s] == dfn[s]) { int tmp = Stack[top--]; instack[tmp] = false; belong[tmp] = ++tot; while (tmp != s) { tmp = Stack[top--]; instack[tmp] = false; belong[tmp] = tot; } } } int main() { read(n), read(m); for (int i = 1; i <= m; i++) { int x; read(x); selected[x] = true; } tarjan(goal = (1 << n) - 1); for (int i = 0; i <= goal; i++) if (selected[i]) ans.insert(belong[i]); writeln(ans.size()); return 0; }
【Div.1 D】Perfect Encoding
【思路要点】
- 首先,我们认为答案中只会包含2和3两种元素,更大的元素可以被拆分成2和3的和,并且方案不会变劣。
- 同时,若答案中出现了至少3个2,我们可以将它们替换为2个3,方案同样不会变劣。
- 枚举使用2的个数(0,1,2),问题转化为求解\(P=\lceil Log_3N \rceil\)。
- 我们对\(P\)进行一次估算,令\(P=max\{\frac{N}{Log_310}-4,0\}\)。
- 如此一来,\(3^P\)就是一个小于\(N\),而又差不太多的数,我们可以通过不断地对其乘以3来得到最终答案。
- 求解\(3^P\)需要用到FFT快速幂。
- 注意特判\(N=1\)的情况。
- 时间复杂度\(O(|N|Log|N|)\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 4194304; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } namespace FFT { const int MAXN = 4194304; const double pi = acos(-1); struct point {double x, y; }; point operator + (point a, point b) {return (point) {a.x + b.x, a.y + b.y}; } point operator - (point a, point b) {return (point) {a.x - b.x, a.y - b.y}; } point operator * (point a, point b) {return (point) {a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x}; } point operator / (point a, double x) {return (point) {a.x / x, a.y / x}; } int N, Log, home[MAXN]; point tmp[MAXN]; void FFTinit() { for (int i = 0; i < N; i++) { int tmp = i, ans = 0; for (int j = 1; j <= Log; j++) { ans <<= 1; ans += tmp & 1; tmp >>= 1; } home[i] = ans; } } void FFT(point *a, int mode) { for (int i = 0; i < N; i++) if (home[i] < i) swap(a[i], a[home[i]]); for (int len = 2; len <= N; len <<= 1) { point delta = (point) {cos(2 * pi / len * mode), sin(2 * pi / len * mode)}; for (int i = 0; i < N; i += len) { point now = (point) {1, 0}; for (int j = i, k = i + len / 2; k < i + len; j++, k++) { point tmp = a[j]; point tnp = a[k] * now; a[j] = tmp + tnp; a[k] = tmp - tnp; now = now * delta; } } } if (mode == -1) { for (int i = 0; i < N; i++) a[i] = a[i] / (4 * N); } } void times(int *a, int *b, int *c, int limit) { N = 1, Log = 0; while (N < 2 * limit) { N <<= 1; Log++; } for (int i = 0; i < limit; i++) tmp[i] = (point) {(double) (a[i] + b[i]), (double) (a[i] - b[i])}; for (int i = limit; i < N; i++) tmp[i] = (point) {0, 0}; FFTinit(); FFT(tmp, 1); for (int i = 0; i < N; i++) tmp[i] = tmp[i] * tmp[i]; FFT(tmp, -1); for (int i = 0; i < N; i++) c[i] = (int) (tmp[i].x + 0.5); } void getmul(int *a, int &lena, int p) { a[0] = 1; static int b[MAXN]; b[0] = 3; lena = 1; int lenb = 1; while (p != 0) { if (p & 1) { times(a, b, a, max(lena, lenb)); lena += lenb + 15; for (int i = 0; i < lena; i++) { a[i + 1] += a[i] / 10; a[i] %= 10; } while (a[lena - 1] == 0) lena--; } p >>= 1; times(b, b, b, lenb); lenb += lenb + 15; for (int i = 0; i < lenb; i++) { b[i + 1] += b[i] / 10; b[i] %= 10; } while (b[lenb - 1] == 0) lenb--; } } } char s[MAXN]; int l; int a[MAXN], lena, ansa; int b[MAXN], lenb, ansb; int c[MAXN], lenc, ansc; void multiply(int *a, int &len, int &ans, int val) { ans += val; for (int i = 0; i < len; i++) a[i] *= val; for (int i = 0; i < len; i++) { a[i + 1] += a[i] / 10; a[i] %= 10; } if (a[len] != 0) len++; } bool Greater(int *a, int len) { if (len > l) return true; if (len < l) return false; for (int i = len - 1; i >= 0; i--) { if (a[i] > s[i] - '0') return true; if (a[i] < s[i] - '0') return false; } return true; } int main() { scanf("%s", s); l = strlen(s); reverse(s, s + l); if (l == 1 && s[0] == '1') { printf("1\n"); return 0; } int p = max(l / log10(3) - 4, 0.0); FFT::getmul(a, lena, p); memcpy(b, a, sizeof(b)); memcpy(c, a, sizeof(c)); lenb = lenc = lena; ansa = ansb = ansc = p * 3; while (!Greater(a, lena)) multiply(a, lena, ansa, 3); multiply(b, lenb, ansb, 2); while (!Greater(b, lenb)) multiply(b, lenb, ansb, 3); multiply(c, lenc, ansc, 2); multiply(c, lenc, ansc, 2); while (!Greater(c, lenc)) multiply(c, lenc, ansc, 3); printf("%d\n", min(min(ansa, ansb), ansc)); return 0; }
【Div.1 E】Prince's Problem
【思路要点】
- \(x\)到\(y\)的路径积,可以转换为\(root\)到\(x\)和\(y\)的路径积除以\(root\)到\(Lca\)和\(father_{Lca}\)的路径积。
- 考虑如何计算\(root\)到\(x\)上每一个点与\(A_i\)的gcd之积。
- 显然,问题对于每一个质因数是独立的,将\(V=10^7\)以内的质数筛出(约\(10^6\)个),对于每个质数\(p\),开一个大小为\(Log_p10^7\)的数组\(Cnt_p\),记录\(root\)到当前节点的路径上,\(p^1\),\(p^2\)……分别出现了多少次。
- 在DFS的过程中,我们可以顺便维护这个数组。
- 将询问离线,我们也可以在访问到\(x\)时处理\(root\)到\(x\)的询问,此时的数组记录的就是\(root\)到\(x\)的信息。
- 对于每个询问,暴力扫描所有\(A_i\)中出现的质因数对应的\(Cnt\)数组,求得其在答案中的次数即可。
- 时间复杂度\(O(NLog^2V+N\sqrt{V}+V)\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 100005; const int MAXLOG = 30; const int MAXP = 1e6 + 5; const int MAXV = 1e7 + 5; const int P = 1e9 + 7; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } struct querys {int home, val; bool type; }; vector <int> a[MAXN]; vector <querys> q[MAXN]; int m, ans[MAXN]; int tot, prime[MAXP], f[MAXV]; int val[MAXN], cnt[MAXP][MAXLOG]; int n, depth[MAXN], father[MAXN][MAXLOG]; int power(int x, int y) { if (y == 0) return 1; int tmp = power(x, y / 2); if (y % 2 == 0) return 1ll * tmp * tmp % P; else return 1ll * tmp * tmp % P * x % P; } void modify(int val, int d) { for (int i = 1; prime[i] * prime[i] <= val; i++) if (val % prime[i] == 0) { int p = 0; while (val % prime[i] == 0) { val /= prime[i]; p++; } cnt[i][p] += d; } if (val != 1) { int pos = lower_bound(prime + 1, prime + tot + 1, val) - prime; cnt[pos][1] += d; } } int query(int val) { int ans = 1; for (int i = 1; prime[i] * prime[i] <= val; i++) if (val % prime[i] == 0) { int p = 0; while (val % prime[i] == 0) { val /= prime[i]; p++; } int tans = 0; for (int j = 1; j <= p; j++) tans += cnt[i][j] * j; for (int j = p + 1; j < MAXLOG; j++) tans += cnt[i][j] * p; ans = 1ll * ans * power(prime[i], tans) % P; } if (val != 1) { int pos = lower_bound(prime + 1, prime + tot + 1, val) - prime, tans = 0; for (int j = 1; j < MAXLOG; j++) tans += cnt[pos][j]; ans = 1ll * ans * power(prime[pos], tans) % P; } return ans; } void work(int pos, int fa) { modify(val[pos], 1); for (unsigned i = 0; i < q[pos].size(); i++) if (q[pos][i].type) ans[q[pos][i].home] = 1ll * ans[q[pos][i].home] * query(q[pos][i].val) % P; else ans[q[pos][i].home] = 1ll * ans[q[pos][i].home] * power(query(q[pos][i].val), P - 2) % P; for (unsigned i = 0; i < a[pos].size(); i++) if (a[pos][i] != fa) work(a[pos][i], pos); modify(val[pos], -1); } void dfs(int pos, int fa) { father[pos][0] = fa; depth[pos] = depth[fa] + 1; for (int i = 1; i < MAXLOG; i++) father[pos][i] = father[father[pos][i - 1]][i - 1]; for (unsigned i = 0; i < a[pos].size(); i++) if (a[pos][i] != fa) dfs(a[pos][i], pos); } int lca(int x, int y) { if (depth[x] < depth[y]) swap(x, y); for (int i = MAXLOG - 1; i >= 0; i--) if (depth[father[x][i]] >= depth[y]) x = father[x][i]; if (x == y) return x; for (int i = MAXLOG - 1; i >= 0; i--) if (father[x][i] != father[y][i]) { x = father[x][i]; y = father[y][i]; } return father[x][0]; } int main() { for (int i = 2; i < MAXV; i++) { if (f[i] == 0) prime[++tot] = f[i] = i; for (int j = 1; j <= tot && prime[j] <= f[i]; j++) { int tmp = prime[j] * i; if (tmp >= MAXV) break; f[tmp] = prime[j]; } } read(n); for (int i = 2; i <= n; i++) { int x, y; read(x), read(y); a[x].push_back(y); a[y].push_back(x); } for (int i = 1; i <= n; i++) read(val[i]); dfs(1, 0); read(m); for (int i = 1; i <= m; i++) { ans[i] = 1; int x, y, z; read(x), read(y), read(z); int Lca = lca(x, y); q[x].push_back((querys) {i, z, true}); q[y].push_back((querys) {i, z, true}); q[Lca].push_back((querys) {i, z, false}); q[father[Lca][0]].push_back((querys) {i, z, false}); } work(1, 0); for (int i = 1; i <= m; i++) writeln(ans[i]); return 0; }
【Div.1 F】Oppa Funcan Style Remastered
【思路要点】
- 题目中的\(f(i)\)是一个置换,也就是若干个环构成的图。
- 现在题目要求每一个环长度不为1,且能整除\(K\)。
- 不妨令环长均为\(K\)的质因数(否则我们可以将一个大环拆成若干个小环使得环长均为\(K\)的质因数)。
- 令\(K\)的质因数分别为\(p_1\),\(p_2\),…,\(p_{cnt}\),问题本质上是询问\(p_1x_1+p_2x_2+...+p_{cnt}x_{cnt}=N\)是否有非负整数解。
- 当\(Cnt=0\),也即\(K=1\),问题无解。
- 当\(Cnt=1\),问题有解等价于\(p_1\)能整除\(N\)。
- 当\(Cnt=2\),问题可以通过exgcd求解。
- 否则,有\(p_1≤10^5\),我们对\(p_1\)的剩余系建立一张图,每个点表示一个模\(p_1\)的同余类,若\(x+p_i\equiv y(Mod\ p_1)\),连接\(x\Rightarrow y\),边长为\(p_i\)。
- 在这张图上运行从0号点开始的单源最短路,每个点\(i\)对应的最短路即为用\(p_1\),\(p_2\),…,\(p_{cnt}\)能够组合出来的模\(p_1\)余\(i\)的最小的数。
- 据此判断\(N\)是否能够被组合出来即可。
- 注意\(K\)较大,我们需要将所有\(\sqrt{K}\)以内的质数筛出来,再对\(K\)进行质因数分解,单次质因数分解的时间复杂度由\(O(\sqrt{K})\)降至\(O(\frac{\sqrt{K}}{LogK})\)。
- 令\(Q=50\),总时间复杂度为\(O(\sqrt{K}+TLogN+Q*(\frac{\sqrt{K}}{LogK}+K^{\frac{1}{3}}LogK))\)
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXQ = 55; const int MAXLOG = 64; const int MAXN = 100005; const int MAXP = 2e6 + 5; const int MAXV = 3.2e7 + 5; const long long INF = 4e18; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int q; long long memk[MAXQ]; int tot, prime[MAXP], f[MAXV]; int cnt[MAXQ]; long long p[MAXQ][MAXLOG]; long long dist[MAXQ][MAXN]; struct info {long long dist; int home; }; bool operator < (info a, info b) { return a.dist > b.dist; } void exgcd(long long a, long long b, long long &x, long long &y) { if (b == 0) { x = 1; y = 0; return; } long long q = a / b, r = a % b; exgcd(b, r, y, x); y -= q * x; } int main() { for (int i = 2; i < MAXV; i++) { if (f[i] == 0) prime[++tot] = f[i] = i; for (int j = 1; j <= tot && prime[j] <= f[i]; j++) { int tmp = prime[j] * i; if (tmp >= MAXV) break; f[tmp] = prime[j]; } } int T; read(T); while (T--) { long long n, k; read(n), read(k); int pos = 0; for (int i = 1; i <= q; i++) if (memk[i] == k) pos = i; if (pos == 0) { pos = ++q; memk[q] = k; long long tmp = k; for (int i = 1; 1ll * prime[i] * prime[i] <= tmp; i++) if (tmp % prime[i] == 0) { p[pos][++cnt[pos]] = prime[i]; while (tmp % prime[i] == 0) tmp /= prime[i]; } if (tmp != 1) p[pos][++cnt[pos]] = tmp; if (cnt[pos] >= 3) { for (int i = 0; i < p[pos][1]; i++) dist[pos][i] = INF; static priority_queue <info> Heap; dist[pos][0] = 0; Heap.push((info) {0, 0}); static bool vis[MAXN]; memset(vis, false, sizeof(vis)); while (!Heap.empty()) { while (!Heap.empty() && vis[Heap.top().home]) Heap.pop(); if (Heap.empty()) break; info tmp = Heap.top(); Heap.pop(); for (int i = 2; i <= cnt[pos]; i++) { int dest = (tmp.home + p[pos][i]) % p[pos][1]; if (dist[pos][dest] > tmp.dist + p[pos][i]) { dist[pos][dest] = tmp.dist + p[pos][i]; Heap.push((info) {dist[pos][dest], dest}); } } } } } bool flg = false; for (int i = 1; i <= cnt[pos]; i++) if (n % p[pos][i] == 0) { printf("YES\n"); flg = true; break; } if (flg) continue; if (cnt[pos] <= 1) { printf("NO\n"); continue; } if (cnt[pos] == 2) { long long x = 0, y = 0; exgcd(p[pos][1], p[pos][2], x, y); y = (y % p[pos][1] + p[pos][1]) % p[pos][1]; long long tmp = y * (n % p[pos][1]) % p[pos][1] * p[pos][2]; if (tmp <= n) printf("YES\n"); else printf("NO\n"); continue; } int tmp = n % p[pos][1]; if (dist[pos][tmp] <= n) printf("YES\n"); else printf("NO\n"); } return 0; }