【CodeForces】CodeForces Round #485 (Div. 1 + Div. 2) 题解

【比赛链接】

【题解链接】

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值