A. Milica and String 大致题意 有一个 A/B 组成的字符串,每次允许选择前 $n$ 个字母,将他们都变成 A/B,问最少的操作次数
思路 简单题,最简单的方式就是枚举每一种可能,计算结果是否符合预期就行
AC code 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 void solve () { int _; cin >> _; for (int ts = 0 ; ts < _; ++ts) { int n, k; cin >> n >> k; string str; str.reserve (n); cin >> str; int cntB = 0 ; for (int i = 0 ; i < n; ++i) cntB += str[i] == 'B' ; if (cntB == k) { cout << 0 << endl; continue ; } const bool useA = cntB > k; for (int i = 0 ; i < n; ++i) { if (cntB > k) { cntB -= str[i] == 'B' ; } else { cntB += str[i] != 'B' ; } if (cntB == k) { cout << 1 << endl; cout << i + 1 << ' ' << (useA ? 'A' : 'B' ) << endl; break ; } } } }
B. Milena and Admirer 大致题意 允许不断的差分一个数组中的值,问至少需要拆多少次,才能让数组非递减
思路 也比较简单,从后往前遍历,如果当前值比后面的值大,则均匀的拆成 $x$ 份,使得恰好比后面的值小或者相同即可
AC code 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #define int long long void solve () { int _; cin >> _; for (int ts = 0 ; ts < _; ++ts) { int n; cin >> n; vector<int > data (n) ; for (auto & i: data) cin >> i; int ans = 0 ; for (int i = n - 2 ; i >= 0 ; --i) { if (data[i] > data[i + 1 ]) { const int cut = data[i] / data[i + 1 ] + (data[i] % data[i + 1 ] == 0 ? 0 : 1 ); data[i] /= cut; ans += cut - 1 ; } } cout << ans << endl; } }
C. Colorful Grid 大致题意 有一个棋盘,允许在棋盘的边上染色,然后使得从最左上角到最右下角的某一条路径长度恰好为 $k$ 的同时,路径上一定上红蓝间隔染色的
思路 即然可以重复点,那么最好的办法是找一个可以旋转的环,在里面转到足够多次就行。
因为需要红蓝间隔,那么必然最小的环就是 $4$ 个单位长度的格子,所以如果 $k$ 恰好是最短的距离加上 $4n$ 的话,就可以这样解决
但是还有一种情况,也就是不满足 $4n$ 的时候,例如 $3 \times 2$ 的方格,走 5 步,也是可以到达的(可以自己绘制一下)
故所以需要兼容上面的两种情况,我给出的一种解法如下
首先根据矩形的长边,旋选择左边或者右边的,然后固定将左上角绘制成上述形状,然后再补充移动到右下角的路径即可
AC code 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 void solve () { int _; cin >> _; for (int ts = 0 ; ts < _; ++ts) { int n, m, k; cin >> n >> m >> k; const int mi = n - 1 + m - 1 ; if (k < mi) { cout << "NO" << endl; continue ; } if (const int cut = k - mi; cut != 0 && cut % 2 == 1 ) { cout << "NO" << endl; continue ; } if (n == 2 && m == 2 ) { if (k % 4 != 2 ) cout << "NO" << endl; else cout << "B\nB\nR R" << endl; continue ; } vector<vector<char >> a (n), b (n - 1 ); for (auto & i: a) i.resize (m - 1 ); for (auto & i: b) i.resize (m); if (n >= m) { a[0 ][0 ] = a[1 ][0 ] = a[2 ][0 ] = b[0 ][0 ] = 'B' ; b[0 ][1 ] = b[1 ][0 ] = b[1 ][1 ] = 'R' ; for (int i = 1 ; i < m - 1 ; ++i) a[2 ][i] = a[2 ][i - 1 ] == 'R' ? 'B' : 'R' ; const char last = b[1 ][m - 1 ]; b[1 ][m - 1 ] = a[2 ][m - 2 ]; for (int i = 2 ; i < n - 1 ; ++i) b[i][m - 1 ] = b[i - 1 ][m - 1 ] == 'R' ? 'B' : 'R' ; b[1 ][m - 1 ] = last; } else { a[0 ][0 ] = b[0 ][0 ] = b[0 ][1 ] = b[0 ][2 ] = 'R' ; a[0 ][1 ] = a[1 ][0 ] = a[1 ][1 ] = 'B' ; for (int i = 1 ; i < n - 1 ; ++i) b[i][2 ] = b[i - 1 ][2 ] == 'R' ? 'B' : 'R' ; const char last = a[n - 1 ][1 ]; a[n - 1 ][1 ] = b[n - 2 ][2 ]; for (int i = 2 ; i < m - 1 ; ++i) a[n - 1 ][i] = a[n - 1 ][i - 1 ] == 'R' ? 'B' : 'R' ; a[n - 1 ][1 ] = last; } cout << "YES" << endl; for (const auto & i: a) { for (const auto & j: i) cout << (j ? j : 'R' ) << ' ' ; cout << endl; } for (const auto & i: b) { for (const auto & j: i) cout << (j ? j : 'R' ) << ' ' ; cout << endl; } } }
D. Absolute Beauty 大致题意 有两个数组,允许交换 $b$ 数组中的两个值一次,问使得 $\sum^n_{i=1} \left | a_i - b_i \right |$ 最大的可能是多少
思路 首先得画几个图来理解,为了方便,此处先假定 $a_i < b_i$(后续可以证明可以恒定满足此等式)
那么可以得到主要有两种情况
可见,只有右边的情况是能够真正有意义的,有意义的部分是 $a_j - b_i$
那么就需要找到最大的 $a_j - b_i$ 即可
然后我们再来看看如何证明最开始说的 $a_i < b_i$。你可以尝试将图里的 $a,b$ 交换位置,你会发现对最终的结果没有影响
AC code 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #define int long long void solve () { int _; cin >> _; for (int ts = 0 ; ts < _; ++ts) { int n; cin >> n; vector<int > a (n) , b (n) ; for (auto & i: a) cin >> i; for (auto & i: b) cin >> i; for (int i = 0 ; i < n; ++i) if (a[i] > b[i]) swap (a[i], b[i]); int mib = 0 , maa = 0 ; for (int i = 0 ; i < n; ++i) { if (b[i] < b[mib]) mib = i; if (a[i] > a[maa]) maa = i; } int ans = 0 ; for (int i = 0 ; i < n; ++i) ans += abs (a[i] - b[i]); ans += max (0LL , 2 * (a[maa] - b[mib])); cout << ans << endl; } }
E. Sofia and Strings 大致题意 有一个字符串 $a$,允许你无数次操作如下的两个方法其中之一
问是否能够变成 $b$ 字符串
思路 这里的选择片段排序,实际上最有用的就是选择两个相邻的字母排序,这样就可以最小的改动的情况下,将一个值往前移动
而需要达成这个目标,就意味着每次移动的时候,前面的值都需要比当前值大,否则前面的值只能删除。
故可以考虑遍历 $b$ 的字母,找到当前可用的最小的在 $a$ 中的位置,并将前面的那些字符移动到后面(比当前字母大),或者删除
AC code 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 void solve () { int _; cin >> _; for (int ts = 0 ; ts < _; ++ts) { int n, m; cin >> n >> m; string sn, sm; sn.reserve (n); sm.reserve (m); cin >> sn >> sm; vector<queue<int >> v (26 ); for (int i = 0 ; i < n; ++i) v[sn[i] - 'a' ].push (i); bool flag = true ; for (int i = 0 ; i < m; ++i) { if (v[sm[i] - 'a' ].empty ()) { flag = false ; break ; } int t = v[sm[i] - 'a' ].front (); v[sm[i] - 'a' ].pop (); for (int j = 0 ; j < sm[i] - 'a' ; ++j) while (!v[j].empty () && v[j].front () < t) v[j].pop (); } cout << (flag ? "YES" : "NO" ) << endl; } }