Codeforces Good Bye 2021
Good Bye 2021
A. Interger Diversity
题意
给定 \(n\) 个整数,你可以选择其中的任意项,使其变成它的相反数(如把 \(x\) 变成 \(-x\)) ,问操作后的序列中最多有多少个不同的数字。
分析
记录每个数字是否出现过,如果出现过而相反数没有出现过就把它变成相反数。
Code
/* 终点是一切概率的结束,也是一切期望的开始 */
#include <bits/stdc++.h>
using namespace std;
void solve()
{
int n, res = 0; cin >> n;
map<int, bool> ex;
for (int i = 1; i <= n; i ++ )
{
int x; cin >> x;
if (ex[x] && ex[-x]) continue;
res ++ ;
if (!ex[x]) ex[x] = true; else ex[-x] = true;
}
cout << res << endl;
}
signed main()
{
cout.tie(0)->sync_with_stdio(0);
int _; for (cin >> _; _--;) solve();
return 0;
}
B. Mirror in the String
题意
给定长度为 \(n\) 的字符串 \(s_1s_2 \ldots s_n\) ,选择一个数字 \(k(1 \le k \le n)\) ,使其变成 \(s_1s_2 \ldots s_ks_ks_{k-1} \ldots s_1\) 。问操作后能得到的字符串的最小字典序为多少。
分析
-
\(n = 1 \ or \ s_1 = s_2\)
选择 \(k=1\) 即可得到字典序最小,因为选择 \(k(k \ge 2)\) 时,前两个字符依然是 \(s_1s_2\) ,后面又多了字符导致字典序不是最小。
-
\(s1 \ != \ s2\)
对于字符 \(s_i\) ,如果 \(s_{i+1} \gt s_i\) ,那么选择 \(k=i\) ,因为如果选择 \(k \gt i\) ,那么得到的字符串第 \(i+1\) 位一定变大了,就不是字典序最小。
Code
/* 终点是一切概率的结束,也是一切期望的开始 */
#include <bits/stdc++.h>
using namespace std;
void solve()
{
int n; cin >> n;
string s; cin >> s;
if (n == 1 || s[0] == s[1]) return cout << s[0] << s[0] << endl, void();
int p = 0;
while(p < n - 1 && s[p + 1] <= s[p]) p ++ ;
for (int i = 0; i <= p; i ++ ) cout << s[i];
for (int i = p; i >= 0; i -- ) cout << s[i];
cout << endl;
}
signed main()
{
cout.tie(0)->sync_with_stdio(0);
int _; for (cin >> _; _--;) solve();
return 0;
}
C. Representative Edges
题意
给定长度为 \(n\) 的序列 \(a\) ,每次操作可以修改其中某一个元素的值,问最小操作多少次,使得对于任意 \(1 \le l \le r \le n\) ,满足 \(a_l + a_{l+1} + \ldots + a_r = \dfrac 1 2(a_l + a_r) \times (r - l + 1)\) 。
分析
可以发现题目要求的性质为等差数列的性质,题目变为:求最少修改多少次使得序列变为等差数列。
由于数据很小 \(1 \le n \le 70\) 。我们可以固定数列任意两项作为要求的等差数列的一部分,求出其他满足该等差数列性质的个数。
假设固定 \(i, j\) ,那么对于任意一项 \(k\) ,满足等差数列性质需要: $\dfrac {a_j - a_i} {j-i} = \dfrac {a_k - a_i} {k - i} $ ,即 \((a_j - a_i) \times (k - i) = (a_k - a_i) \times (j - i)\) 。
Code
/* 终点是一切概率的结束,也是一切期望的开始 */
#include <bits/stdc++.h>
#define rep(i, x, y) for (int i = x; i <= y; i ++ )
using namespace std;
const int N = 1000, M = 400010, mod = 998244353, INF = 0x3f3f3f3f;
int a[N];
void solve()
{
int n, ret; cin >> n; ret = max(0, n - 2);
for (int i = 1; i <= n; i ++ ) cin >> a[i];
rep(i, 1, n) rep(j, i + 1, n)
{
int save = 0;
rep(k, 1, n) if ((a[k] - a[i]) * (j - i) == (a[j] - a[i]) * (k - i)) ++ save;
ret = min(ret, n - save);
}
cout << ret << endl;
}
signed main()
{
cout.tie(0)->sync_with_stdio(0);
int _; for (cin >> _; _--;) solve();
return 0;
}
D. Keep the Average High
题意
给定长度为 \(n\) 的序列 \(a\) ,选出最多的元素,满足对于任意 \(1\le l \lt r \le n\) (\(l, r\) 均指原序列的下标),满足以下至少一项:
- 存在至少一项 \(l \le k \le r\) 没有被选择。
- \(a_l + a_{l+1} \ldots a_r \ge x \times (r - l + 1)\) 。
问能够选择最多多少个元素。
分析
对于任意 \(l, r\) ,如果其中有一个没有被选择,那么一定满足限制,所以只需要考虑 \([l, r]\) 都被选择时是否满足限制。
注意第 \(2\) 个限制,等价于 \((a_l - x) + (a_{l + 1} - x) + \ldots + (a+r - x) \ge 0\) 。
注意到 \([l, r]\) 是连续的,我们设 \(p(i) = \sum_{i=1}^i a_i - x\) ,那么上述限制等价于 \(p(r) - p(l-1) \ge 0\) 。
假设目前枚举到第 \(i\) 位,那么我们需要考虑后两位,如果 \(p(i + 2) - p(i) \lt 0\) ,即 \(a(i+1) + a(i+2) \lt 0\) ,那么我们不能全部取到,否则令 \(l = i+1, r=i+2\) ,不满足限制。如果满足 \(p(i+2) - p(i) \ge 0\) ,因为前面满足的序列加上第 \(i+1\) 位不一定不小于 \(0\) ,那么我们取最大值,把 \(i+1\) 位放到待判断的序列中,即:
\(a_1 \ldots a_qq_{q+1} \ldots a_i \ldots a_n\) ,区间 \([q, i]\) 为满足限制的区间,而 \(i+1\) 进入到待选择区域。这样我们就不仅只考虑满足限制的最后一个位置后面的两个位置了。对于带选择区间 \([l, r]\) ,因为加上了 \(a_r\) 导致 \(p(r) - p(l-1) < 0\) ,那么只需要不选择 \(r\) 即可,因为 \([l, r-1]\) 加上前面满足的区间依然不小于零。
Code
/* 终点是一切概率的结束,也是一切期望的开始 */
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 200010;
int a[N], pre[N];
void solve()
{
int n, x, ret, mx = 0; cin >> n; ret = n;
for (int i = 1; i <= n; i ++ ) cin >> a[i];
cin >> x;
for (int i = 1; i <= n; i ++ ) pre[i] = pre[i-1] + a[i] - x;
for (int i = 2; i <= n; i ++ )
if (pre[i] < mx) { -- ret; mx = pre[i]; ++ i; }
else mx = max(mx, pre[i-1]);
cout << ret << endl;
}
signed main()
{
cout.tie(0)->sync_with_stdio(0);
int _; for (cin >> _; _--;) solve();
return 0;
}