CodeTON Round 7 (Div. 1 + Div. 2, Rated, Prizes!)

1|0基本情况

A题花了快半小时,做出来了但是不如正解。

B题又是老毛病,一条路走到黑,爆搜打出来超时就死命想剪枝和记忆化,没想过换方法(觉得贪心不可行)。

C题其实想的是对的,但是没继续想下去。

2|0A - Jagged Swaps

Problem - A - Codeforces

2|1我的解法

没啥好说的,纯模拟。看到 n10 知道能过。

但是细节上出了一些问题导致快半小时才做出来。

#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; int a[15]; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); int t; cin >> t; while (t--) { int n; cin >> n; bool is_right = true, is_wrong = false; for (int i = 1 ; i <= n; i++) { cin >> a[i]; if (a[i] < a[i - 1]) { is_right = false; } if (a[i] == a[i - 1]) { is_wrong = true; } } if (is_right) { cout << "YES" << endl; continue; } if (is_wrong) { cout << "NO" << endl; continue; } bool toDo = false; bool is_solve = true; do { is_solve = true; toDo = false; for (int i = 2; i <= n - 1; i++) { if (a[i] > a[i + 1]) { if (a[i] > a[i - 1]) { toDo = true; swap(a[i], a[i + 1]); } } } for (int i = 2; i <= n; i++) { if (a[i] < a[i - 1] || a[i] == a[i - 1]) { is_solve = false; } } } while (toDo); if (!is_solve) { cout << "NO" << endl; } else { cout << "YES" << endl; } } return 0; }

2|2更优解法

Observe that since we are only allowed to choose i2 to swap ai and ai+1, it means that a1 cannot be modified by the operation. Hence, a1=1 must hold. In fact, we can prove that as long as a1=1, we will be able to sort the array.

Consider the largest element of the array. Let its index be i. Our objective is to move ai to the end of the array. If i=n, it means that the largest element is already at the end. Otherwise, since ai is the largest element, this means that ai1<ai and ai>ai+1. Hence, we can do an operation on index i and move the largest element one step closer to the end.

We repeatedly do the operation until we finally move the largest element to the end of the array. Then, we can pretend that the largest element does not exist and do the same algorithm for the prefix of size n1. Hence, we will able to sort the array by doing this repeatedly.

#include <bits/stdc++.h> using namespace std; typedef long long ll; typedef vector <ll> vi; int main() { int t; cin >> t; while (t --> 0) { int n; cin >> n; vi arr(n); for (int i = 0; i < n; i++) { cin >> arr[i]; } if (arr[0] == 1) { cout << "YES"; } else { cout << "NO"; } cout << '\n'; } }

3|0B - AB Flipping

Problem - B - Codeforces

我的解法,搜索加剪枝,疯狂TLE。

#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #define INF 0x7fffffffffffffff #define re register using namespace std; using ll = long long; inline void Swap(int &a, int &b) { a = a ^ b; b = a ^ b; a = a ^ b; return; } const int N = 2e5 + 10; int n; int a[N]; bool vis[N]; ll ans = -INF; inline bool check(int a, int b) { return a == 65 && b == 66; } inline void dfs(ll now) { if (now < ans) { return; } ans = now; for (re int i = 1; i <= n - 1; i++) { if (check(a[i], a[i + 1]) && !vis[i]) { Swap(a[i], a[i + 1]); vis[i] = true; dfs(now + 1); Swap(a[i], a[i + 1]); vis[i] = false; } } } int main() { int t; scanf("%d", &t); while (t--) { scanf("%d", &n); ans = -INF; char ch; getchar(); for (re int i = 1; i <= n; i++) { ch = getchar(); a[i] = ch; } dfs(0); ans = (ans == -INF) ? 0 : ans; printf("%lld\n", ans); } return 0; }

3|1正确解法

1|0CF标程

If the string consists of only A or only B, no operations can be done and hence the answer is 0.

Otherwise, let x be the smallest index where sx=A and y be the largest index where sy=B

If x>y, this means that the string is of the form s=BBAA Since all the B are before the A, no operation can be done and hence the answer is also 0.

Now, we are left with the case where x<y. Note that s[1,x1]=BB and s[y+1,n]=AA by definition. Since the operation moves A to the right and B to the left, this means that s[1,x1] will always consist of all B and s[y+1,n] will always consist of all A. Hence, no operation can be done from index 1 to x1 as well as from index y to n1.

The remaining indices where an operation could possibly be done are from x to y1. In fact, it can be proven that all yx operations can be done if their order is chosen optimally.

Let array b store the indices of s between x and y that contain B in increasing order. In other words, x<b1<b2<<bk=y and bi=B, where k is the number of occurences of B between x and y. For convenience, we let b0=x. Then, we do the operations in the following order:

b11,b12,,b0+1,b0,

b21,b22,,b1+1,b1,

b31,b32,,b2+1,b2,

bk1,bk2,,bk1+1,bk

It can be seen that the above ordering does operation on all indices between x and y1. To see why all of the operations are valid, we look at each row separately. Each row starts with bi1, which is valid as sbi=B and sbi1=A (assuming that it is not the last operation of the row). Then, the following operations in the same row move A to the left until position bi1. To see why the last operation of the row is valid as well, even though sbi1 might be equal to B initially by definition, either i=1 which means that sb0=sx=A, or an operation was done on index bi11 in the previous row which moved A to index bi11. Hence, all operations are valid.

#include <bits/stdc++.h> using namespace std; char s[200005]; signed main() { ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0); int tc, n; cin >> tc; while (tc--) { cin >> n; s[n + 1] = 'C'; for (int i = 1; i <= n; ++i) cin >> s[i]; int pt1 = 1, pt2 = 1, ans = 0; while (s[pt1] == 'B') ++pt1, ++pt2; while (pt1 <= n) { int cntA = 0, cntB = 0; while (s[pt2] == 'A') ++pt2, ++cntA; while (s[pt2] == 'B') ++pt2, ++cntB; if (s[pt2 - 1] == 'B') ans += pt2 - pt1 - 1; if (cntB) pt1 = pt2 - 1; else break; } cout << ans << '\n'; } }

1|0某佬写法

首先,在前面出现的 B 和在后面出现的 A 肯定没用。

设第一个 A 出现的下标为 l,第一个 B 出现的下标为 r,那么对于 [l,r) 之间的每个 i,都能进行一次操作。

证明:

首先我们考虑一个前面是 A 后面是 Bs。众所周知,s 能进行 |s|1 次操作。

而我们就可以把 [l,r] 分割成 x 个上述的串,则根据上述结论,可以进行 rlx 次操作。

我们继续考虑哪儿能进行操作:

我们发现,每个上述串执行完操作后第一项必为 B 最后一项必为 A,所以在两个串的“接口”还能进行一次操作,也就是说,在 [l,r] 中还能进行 x1 次操作作,总共就能进行 rlx+x1=rl1 次操作。

#include <bits/stdc++.h> using namespace std; using LL = long long; const int INF = 0x3f3f3f3f; const LL mod = 1e9 + 7; const int N = 300005; char s[N]; int main() { int _; scanf("%d", &_); while (_--) { int n; scanf("%d%s", &n, s + 1); int ans = 0, x = 0, y = 0; for (int i = 1; i <= n; i++) { if (s[i] == 'B') y = i; else { if (x == 0) x = i; } } if (x != 0 && y != 0) { if (y >= x) ans = y - x; } printf("%d\n", ans); } return 0; }

4|0C-Matching Arrays

Problem - C - Codeforces

4|1我的想法

ab 序列先排序,然后用贪心。

但是当时主要是觉得解完之后再恢复原序很麻烦,又觉得B题可解,所以没有继续写。

4|2推进正解

贪心其实很明显,拿 a 的第 k 大来比 b 的第 k 小,只要都满足,就能满足 a 序列中 m 个数比 b 大。

但是还要反向论证,必须除了这 m 个数以外,其他 nm 个数都是序列 a 小于等于序列 b

恢复原序问题的方法非常巧妙。

首先初始化一个 c 数组,内容即 a 的下标,显然一开始是 1n

iota(c + 1, c + n + 1, 1);\\快速赋值1~n

然后直接对 c 数组排序,排序的比较方式是 a 数组的由小到大。

效果上等于说存储了排序后的 a 数组的排序前的下标。

然后后面用的时候直接通过 c 来下标调用 a 即可。

这样操作就可以在答案数组上直接通过 c 来定位 b 应该移动到的位置。

int a[N], b[N], c[N]; int ans[N]; int n, m; void solve() { std::cin >> n >> m; for (int i = 1; i <= n; i++) std::cin >> a[i]; for (int i = 1; i <= n; i++) std::cin >> b[i]; std::iota(c + 1, c + n + 1, 1); std::sort(c + 1, c + n + 1, [](int p1, int p2){ return a[p1] < a[p2]; }); std::sort(b + 1, b + n + 1); for (int i = 1; i <= m; i++) { int x = c[n - m + i]; if (a[x] <= b[i]) {std::cout << "NO\n"; return ;} ans[x] = b[i]; } for (int i = 1; i <= n - m; i++) { int x = c[i]; if (a[x] > b[i + m]) {std::cout << "NO\n"; return ;} ans[x] = b[i + m]; } std::cout << "YES\n"; for (int i = 1; i <= n; i++) std::cout << ans[i] << " "; std::cout << std::endl; }

__EOF__

本文作者Kdlyh
本文链接https://www.cnblogs.com/kdlyh/p/17856566.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   加固文明幻景  阅读(127)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示