2025.7.2
刷题日记
时隔一月,终于可以继续板刷cf了
前段时间一直在应付期末考试,身心俱疲
Codeforces Round 1029 (Div. 3) E. Lost Soul
https://codeforces.com/problemset/problem/2117/E
给定两个数组a和b,可以使用任意次的以下操作,要求让a[ i ] == b[ i ]的个数最大化
- Let a[ i ] = b[ i + 1 ]
- Let b[ i ] = a[ i + 1 ]
- Delete a[ i ] and b[ i ] (at most once)
经过观察可得,如果数组中存在以下三种情况,那么该位置以左的所有索引i都是可以转化的
- a[ i ] == a[ i + 1 ]
- b[ i ] == b[ i + 1 ]
- a[ i ] == b[ i ]
其实我们可以理解为只有第三种情况,因为第一种情况和第二种情况都是为了转化成第三种情况
那么我们其实可以注意到,不只是第一种和第二种情况可以
比如说在a数组中,如果两个相同的数间隔了偶数个数字,那么他们也是可以转化成第三种情况的
所以,我第一遍给出了一个比较暴力但保对的代码,如下所示:
点击查看代码
//====================Solution-bg====================//
void solve () {
int n;
std::cin >> n;
std::vector<int> a(n + 1), b(n + 1);
std::vector<int> ida[n + 1], idb[n + 1];
for (int i = 1; i <= n; i++) {
std::cin >> a[i];
// ida[a[i]].push_back(i);
}
for (int i = 1; i <= n; i++) {
std::cin >> b[i];
// idb[b[i]].push_back(i);
}
if (a[n] == b[n]) {
std::cout << n << '\n';
return;
}
for (int i = 1; i <= n; i++) {
ida[a[i]].push_back(i);
idb[b[i]].push_back(i);
}
for (int i = 1; i <= n; i++) {
std::sort(ida[i].begin(), ida[i].end(), [&] (int a, int b) { return a > b; });
std::sort(idb[i].begin(), idb[i].end(), [&] (int a, int b) { return a > b; });
}
int res = -1;
// without delete
for (int i = n; i >= 1; i--) {
for (auto j : ida[a[i]]) {
if (i > j && (i - j) % 2 == 1) {
res = std::max(res, j);
break;
}
}
if (idb[a[i]].empty()) continue;
for (auto j : idb[a[i]]) {
if (i >= j && (i - j) % 2 == 0) {
res = std::max(res, j);
break;
}
}
}
for (int i = n; i >= 1; i--) {
for (auto j : idb[b[i]]) {
if (i > j && (i - j) % 2 == 1) {
res = std::max(res, j);
break;
}
}
if (ida[b[i]].empty()) continue;
for (auto j : ida[b[i]]) {
if (i >= j && (i - j) % 2 == 0) {
res = std::max(res, j);
break;
}
}
}
// delete
for (int i = n; i >= 1; i--) {
for (auto j : ida[a[i]]) {
if (i > j && (i - j) % 2 == 0) {
res = std::max(res, j);
break;
}
}
if (idb[a[i]].empty()) continue;
for (auto j : idb[a[i]]) {
if (i - j > 1 && (i - j) % 2 == 1) {
res = std::max(res, j);
break;
}
}
}
for (int i = n; i >= 1; i--) {
for (auto j : idb[b[i]]) {
if (i > j && (i - j) % 2 == 0) {
res = std::max(res, j);
break;
}
}
if (ida[b[i]].empty()) continue;
for (auto j : ida[b[i]]) {
if (i - j > 1 && (i - j) % 2 == 1) {
res = std::max(res, j);
break;
}
}
}
if (res != -1)
std::cout << res << '\n';
else
std::cout << 0 << '\n';
}
//====================Solution-ed====================//
交了一发,TLE on test3
后来一直想不出优化的方法,索性去看题解了
然后才意识到,其实非常简单:
既然你都可以删一列了,那么也就是说,你相同的数字之间,间隔奇数个还是偶数个都是可以转化的!
换句话讲,只要数组中出现相同的数,那么他们就是有用的,只需要取最优解就可以了!
由此,我们仅需一次遍历即可计算出答案
我们开一个seen数组标记某个数字是否出现过,在倒序遍历时出现的第一组相同数字,就是最终答案!
正确代码如下:
点击查看代码
//====================Solution-bg====================//
void solve () {
int n;
std::cin >> n;
std::vector<int> a(n + 1), b(n + 1);
std::vector<int> seen(n + 1, 0);
for (int i = 1; i <= n; i++) {
std::cin >> a[i];
}
for (int i = 1; i <= n; i++) {
std::cin >> b[i];
}
if (a[n] == b[n]) {
std::cout << n << '\n';
return;
}
// 由于题中所述,可以删除一列,故两相同数字索引之差为偶数为奇数时均可,故只要出现过就一定可以!!!!!!
int ans = 0;
for (int i = n - 1; i >= 1; i--) {
if (a[i] == b[i] || a[i] == a[i + 1] || b[i] == b[i + 1] || seen[a[i]] || seen[b[i]]) {
ans = i;
break;
}
seen[a[i + 1]] = seen[b[i + 1]] = 1;
}
std::cout << ans << '\n';
}
//====================Solution-ed====================//
Educational Codeforces Round 180 (Rated for Div. 2) C. Coloring Game
https://codeforces.com/problemset/problem/2112/C
给定一个数组,现在Alice可从中任取三个数字染成红色,然后Bob从数组中任取一数字染成蓝色
(如果已经是红色,也可以改为蓝色),计算出Alice必胜的方案数
我们不难看出,Bob有两种决策:
- 将数组中的最大值染为蓝色
- 将已经染红的三个数字中的最大值染为蓝色
这两种决策是Bob的最优解
那么也就是说,对于Alice选的x,y,z三值,假设x <= y <= z,那么Bob要么选z,要么选a数组中的max
这样的话,前者Alice变成x + y,Bob变成z,等价于Alice还是x + y + z,Bob变成2 * z
后者Alice还是x + y + z,Bob变成max(a[ n ])
只要x + y + z > std::max(2 * z, a[ n ])
我们可以枚举每一种情况计算,但是这样会超时
我们可以只枚举y和z,然后用二分查找,在数组中查找有多少个x可以匹配y和z的组合,最后加到ans上即可
代码如下:
点击查看代码
//====================Solution-bg====================//
void solve () {
int n;
std::cin >> n;
std::vector<int> a(n + 1);
for (int i = 1; i <= n; i++) {
std::cin >> a[i];
}
std::sort(a.begin() + 1, a.end());
ll ans = 0;
for (int z = 1; z <= n; z++) {
for (int y = 1; y < z; y++) {
int x = std::max(a[n], 2 * a[z]) - a[y] - a[z];
int k = upper_bound(a.begin(), a.begin() + y, x) - (a.begin());
ans += y - k;
}
}
std::cout << ans << '\n';
}
//====================Solution-ed====================//

浙公网安备 33010602011771号