Educational Codeforces Round 139 (A-D)
A
题意:有t组测试数据,每组测试数据输入一个整数n,输出一个整数,表示[1, n]范围内满足以下条件的x的个数。其中x满足条件"只有一个非0的位数(比如5000, 4, 200)"。
解法/思路:举个例子,若n为四位数,则必然有9个一位数,9个二位数,9个三位数满足条件,而满足条件的四位数就等于n的首位数的值。
代码:
#include <iostream> using namespace std; int main() { int t, n, ans = 0; cin >> t; while (t--) { cin >> n; while (n >= 10) { n /= 10; ans += 9; } ans += n; cout << ans << endl; ans = 0; } return 0; }
B
题意:t组测试数据,每组测试数据输入共两行,第一行输入整数n,第二行输入长度为n的字符串。输出共一行,若该字符串能在操作次数小于n的情况下打出来,则输出"YES",否则输出"NO"。
所谓"操作"有两种:
1.在字符串末端打出一个字符。
2.复制一段前面已经打出来的部分追加到字符串末端。
显然,只要字符串当中出现两个长度为2,完全相同且位置不重合(比如"hhh"中,前面的"hh"和后面的"hh"就是重合了)的子串,就可以断定为"YES"。
解法/思路:本人采用unordered_map(STL大法好( •̀ ω •́ )y),记录所有上述子串及其出现的位置,竟然意外地AC了。
不过其实 unordered_map 完全可以不用,两个小写字母所组成的字符串最多也就 26 * 26 = 676 种,用一个长度为 676 的数组记录即可。
代码:
#include <iostream> #include <string> #include <unordered_map> using namespace std; int main() { int t, len, k = 0; string str, tmp; cin >> t; while (t--) { unordered_map<string, int> mp; cin >> len; cin >> str; for (k = 0; k < len - 1; ++k) { tmp = str.substr(k, 2); if (mp.find(tmp) != mp.end() && k - mp[tmp] > 1) { // 如果该子串出现过且不"重合" cout << "YES" << endl; break; } else { mp.insert(make_pair(tmp, k)); } } if (k == len - 1) { cout << "NO" << endl; } } return 0; }
C
题意:有两排砖块,它们组成一个矩形,原本所有砖块都是白色,现在要求用一只刷子刷上黑色油漆,使得每一列至少有一块是黑的,刷子只能上下左右地移动,且已经刷过的地方就不能再经过了,现在有t组测试数据,每组测试数据输入共三行,第一行是一个整数n,下面两行是两个长度为n,仅由'W'和'B'组成的字符串('B'即表示"Black"),输出共1行,若能够刷成这两个字符串所表示的样子,输出"YES",否则输出"NO"。
解法/思路:找规律。当两个靠的最近的'W'相差的列数为偶数时,如果它们在同一列;或者如果相差的列数为奇数,且不在同一列,则为"NO"。
代码:
#include <iostream> #include <cstdio> #include <string> #include <algorithm> using namespace std; char r1[200001]; char r2[200001]; int x, y = 0, x2, y2 = 0; // x, y, x2, y2用于记录'W'出现的位置,每次遇到'W'时,更新y小的那组数据 int main() { int t, n, k; cin >> t; while (t--) { cin >> n; scanf("%s", r1 + 1); scanf("%s", r2 + 1); for (k = 1; k <= n; ++k) { if (r1[k] == 'W') { if (y < y2) { y = k; x = 1; } else { y2 = k; x2 = 1; } } if (r2[k] == 'W') { if (y < y2) { y = k; x = 2; } else { y2 = k; x2 = 2; } } if (y && y2) { if (x != x2 && ((abs(y2 - y)) % 2 != 0)) { cout << "NO" << endl; break; } if (x == x2 && ((abs(y2 - y)) % 2 == 0)) { cout << "NO" << endl; break; } } } if (k == n + 1) { cout << "YES" << endl; } y = 0; y2 = 0; } return 0; }
D
题意:如果二元组 (a, b) 满足 gcd(a, b) = 1,那么称这个二元组为“幸运对”,如果 (a, b), (a + 1, b + 1), ..., (a + k, b + k),均为“幸运对”,那么称这 n + 1 个幸运对组成了一条长度为 k + 1 的“幸运链”,现在有 n 组数据,每组数据给定两个正整数 a, b,问以 (a, b) 为起点所能构成的最长的幸运链有多长,若长度可以无穷大则输出 -1。
数据范围:n <= 1e6, 1 <= a, b <= 1e7
方便起见,我们默认 a < b。
显然,长度为无穷大的情况就是 b - a = 1,长度为 0 的情况就是 gcd(a, b) != 1。下面我们讨论除这两种之外的情况。
根据“更相减损法”,gcd(a, b) = gcd(a, b - a),那么 gcd(a + k, b + k) = gcd(a + k, b - a),记 b - a 这一定值为 x。如果 gcd(a + k, x) != 1,那么此时 a + k 一定是 x 的某个质因数的倍数,计算“幸运链”的最大长度,就是要找到最小的满足这一条件的整数 k。
再进一步思考,如果 x 的某个质因数为 p,如果 a + k 为 p 的倍数,那么显然 k = p - a % p。枚举 x 所有的质因数就可以得出答案。
现在只剩最后一个问题要解决,如果每组数据都使用试除法来计算 x 的质因数,那么时间复杂度为 O(n√x),n 最大可达 1e6,x 最大可达 1e7 - 1,显然会超时。
如何解决呢?素数的线性筛法中,我们需要利用数组 low 来记录某个数的最小的质因数。程序的一开始,可以先跑一遍线性筛,预处理出 low[1] ~ low[1e7]。这样一来,对于 x,low[x] 就是它最小的质因数。不断用 low[x] 去更新答案,再令 x /= low[x],如此往复,直至 x = 1,一定不会漏掉 x 的某个质因数,而最坏情况下 x 也不过只有 log2(x) 个质因数,时间复杂度被降到了 O(nlogx),可以通过 4s 的时间限制。
代码(输入数据较多,所以我用了快读快写):
#include <bits/stdc++.h> using namespace std; inline void read_int(int& a) { // 快读 int x = 0, s = 0; char c = getchar(); while (c < '0' || c > '9') { if (c == '-') ++s; c = getchar(); } while ('0' <= c && c <= '9') { x = x * 10 + c - '0'; c = getchar(); } a = (s ? -x : x); return; } inline void write_int(int a) { // 快写 static char buf[11]; int p = 0; if (a < 0) { putchar('-'); a = -a; } if (!a) buf[p++] = '0'; else { while (a) { buf[p++] = a % 10 + '0'; a /= 10; } } while (~--p) putchar(buf[p]); return; } int gcd(int a, int b) { while (b) swap(a %= b, b); return a; } int low[10000001]; // low[i] 即 i 的最小的质因数 vector<int> prime; void solve() { int a, b; read_int(a); read_int(b); if (a > b) swap(a, b); if (gcd(a, b) != 1) { puts("0"); return; } if (b - a == 1) { puts("-1"); return; } int x = b - a; int ans = 0x3f3f3f3f; while (x != 1) { ans = min(ans, low[x] - a % low[x]); // 不断用 low[x] 更新答案, 直至 x = 1 x /= low[x]; } write_int(ans); puts(""); } int main() { for (int i = 2; i <= 10000000; ++i) { // 线性筛 if (low[i] == 0) { low[i] = i; prime.push_back(i); } for (size_t j = 0; j < prime.size(); ++j) { if (prime[j] > low[i] || prime[j] > 10000000 / i) break; low[i * prime[j]] = prime[j]; } } int tt = 1; scanf("%d", &tt); while (tt--) { solve(); } return 0; }
迟到了将近 7 个月的补题ヽ(*。>Д<)o゜
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)