总结: 个人觉得难度划分是 A B D C E F
A.tb的区间问题
题意:
对一个数组进行k次删除操作, 对于操作删除只能删除最左元素或者最右元素, 求出k次操作后数组和的最大值
思路:
由于删除最左元素和最右元素那么必然最后得到的数组和是一个连续的区间, 那么删除k 也就是剩余n - k的空间, 通过前缀和预处理得到每一段n - k区间的值取最大值就i是最终答案
复杂度:
O(n)
Code:
#include <bits/stdc++.h>
using namespace std;
void solve() {
using i64 = long long;
int n, k;
cin >> n >> k;
vector <i64> pre(n + 1);
k = n - k;
i64 ans = 0;
for (int i = 1; i <= n; i++) {
int x;
cin >> x;
pre[i] = pre[i - 1] + x;
if (i >= k) {
ans = max(ans, pre[i] - pre[i - k]);
}
}
cout << ans;
}
int main() {
cin.tie(0) -> sync_with_stdio(false);
int t = 1;
// cin >> t;
while (t--) {
solve();
}
return 0;
}
B.tb的字符串问题
题意:
选择子串"fc"和"tb" 从字符串删除, 求出最后剩下字符串的最短长度 (子串:指向的是连续的一段字符串)
思路:
对于连续的一段字符串, 在这里我使用vector(动态数组)来维护这一段, 在删除这两个子串的条件保证数组的长度大于等于2, 其次判断最后两个字符是否符合条件, 如果符合条件则删除, 否则不管进行下一次操作最后输出长度即可
复杂度:
O(n)
Code:
#include <bits/stdc++.h>
using namespace std;
bool check (char a, char b) {
return (a == 'f' && b == 'c') || (a == 't' && b == 'b');
}
void solve() {
int n;
string s;
cin >> n >> s;
vector <char> ver;
for (int i = 0; i < n; i++) {
ver.push_back(s[i]);
if (ver.size() >= 2) {
if (check(ver[ver.size() - 2], ver[ver.size() - 1])) {
ver.pop_back(); ver.pop_back();
}
}
}
cout << ver.size();
}
int main() {
cin.tie(0) -> sync_with_stdio(false);
int t = 1;
// cin >> t;
while (t--) {
solve();
}
return 0;
}
C.tb的路径问题
赛时打表
题意:
从 (1, 1) 出发, 目标是到达 (n, n), 每个格子的值是 gcd(i, j), 如果当前格子的值不为 1,可以传送到其他值相同的格子,不消耗能量。移动到相邻格子(上、下、左、右)需要消耗 1 格体力, 问最少需要多少体力才能从 (1, 1) 到达 (n, n)
思路:
通过手上的多种样例:传送机制允许瞬移到 gcd 值相同的格子, 当 n < 4时, 传送不如直接移动有效,最少步数为2(n - 1),当n >= 4时, 若n为偶数, 通过传送到(n, n - 2)再走 2 步, 总步数为 4, 若n为奇数, 通过传送到(n - 1, n - 3) 再走 4 步, 总步数为 6。传送机制比普通移动更优。
结论:
对于 n >= 4 的证明, 偶数时 (1, 1) 和 (n, n) 无法直接传送, 但通过 (2, 2) 和 (n, n - 2) 的传送阵是最优选择;奇数情况下, 只是多了从 (2, 2) 到 (n - 1, n - 3) 的额外传送选项,步数增加 2
对于 n < 4 就是直接走向终点就是最好的结果 2 * (n - 1)
复杂度:
O(1)
Code:
#include <bits/stdc++.h>
using namespace std;
void solve() {
int n;
cin >> n;
if (n < 4) {
cout << 2 * (n - 1);
} else if (n & 1) {
cout << 4;
} else {
cout << 6;
}
}
int main() {
cin.tie(0) -> sync_with_stdio(false);
int t = 1;
// cin >> t;
while (t--) {
solve();
}
return 0;
}
D.tb的平方问题
题意:
对于q次询问, 求出包含x位置且区间和为完全平方数的数组个数
思路:
通过暴力枚举每个区间判断是否是完全平方数,若满足则使用前缀和与差分数组预处理,最后通过O(1)时间输出结果。
复杂度:
O(n ^ 2 + q)
Code:
#include <bits/stdc++.h>
using namespace std;
bool check (int x) {
int sq = sqrt(x);
return sq * sq == x;
}
void solve() {
int n, q;
cin >> n >> q;
vector <int> a(n + 1), pre(n + 1);
for (int i = 1; i <= n; i++) {
cin >> a[i];
pre[i] = pre[i - 1] + a[i];
}
vector <int> ans(10005);
for (int i = 1; i <= n; i++) {
for (int j = i; j <= n; j++) {
if (check(pre[j] - pre[i - 1])) {
ans[j + 1]--; ans[i] ++;
}
}
}
for (int i = 1; i <= 10000; i++) ans[i] += ans[i - 1];
while (q--) {
int x;
cin >> x;
cout << ans[x] << '\n';
}
}
int main() {
cin.tie(0) -> sync_with_stdio(false);
int t = 1;
// cin >> t;
while (t--) {
solve();
}
return 0;
}
E.tb的数数问题 (筛法很多, 这里只举例一种)
题意:
有多少个"好整数", 好整数的定义是在这个集合里的每一个x的约数都恰好都在, 这个集合的长度就是好整数
思路:
在集合未出现的元素的自己以及自己为约数都不是"好整数", 那么通过线性筛枚举未出现的元素赋值为0, 那么最后统计剩下的元素1的个数就是好整数的个数
复杂度:
O(MlogM) M = 1e6
Code:
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
using i64 = int64_t;
void solve() {
int n;
cin >> n;
constexpr int M = 1e6 + 1;
vector <bool> ver(M, 0);
for (int i = 1, x; i <= n; i++) {
cin >> x;
ver[x] = 1;
}
vector <int> vis(M, 1);
for (int i = 1; i < M; i++) {
if (!ver[i]) {
for (int j = i; j < M; j += i) {
vis[j] = 0;
}
}
}
cout << accumulate(vis.begin() + 1, vis.end(), 0);
}
int main() {
cin.tie(0) -> sync_with_stdio(false);
int t = 1;
// cin >> t;
while (t--) {
solve();
}
return 0;
}
F.tb的排列问题
暂时还不会