Codeforces Round #780 (Div. 3)
Codeforces Round #780 (Div. 3) solution
A - Vasya and Coins
题意
有 \(a\) 个 \(1\) , \(b\) 个 \(2\)。求出最小不能表示的正整数。
数据范围: \(0 \leq a,b \leq 10^8\)。
题解
- 如果 \(a = 0\),答案显然为 \(1\)。
- 如果 \(a \geq 1\),答案为\(2b + a + 1\)。
证明如下:
\(x = 2 \times x + 1\),这样可以表示\([1, 2b + 1]\)的所有整数。
\(x = 2\times b + 1 \times y\),这样可以表示\([2b + 2, 2b + a]\)的所有整数。
程序时间复杂度为 \(O(1)\)。
C++ 代码示例
# include <bits/stdc++.h>
# define int long long
using namespace std;
signed main() {
int T; cin >> T;
while (T--) {
int a, b; cin >> a >> b;
if (a == 0) puts("1");
else
cout << 2 * b + a + 1 << endl;
}
return 0;
}
B - Vlad and Candies
题意
有 \(a[i]\) 个数字 \(i\) , 其中 \(1 \leq i \leq n\)。 是否存在一种取数的方案让相邻取的两个数都不相同。
数据范围:\(1 \leq n \leq 2\times 10^5\)。
题解
可以考虑一组构造,依次选取\(1 - n\),这样的话最后会剩余两个数字。
即最大值和次大值对应的 \(i_{max}, i_{min}\),那么这个时候判定就是\(a[i_{max}] - a[i_{min}]\)和 \(1\) 比大小的问题了。
时间复杂度: \(O(n \log_2 n)\) or \(O(n)\)。
C++ 代码示例
# include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int a[N];
int main() {
int T; cin >> T;
while (T--) {
int n; cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
sort(a + 1, a + 1 + n);
if (a[n] - a[n - 1] > 1) puts("NO"); else puts("YES");
}
return 0;
}
C - Get an Even String
题意
如果一个字符串长度为偶数,且对于奇数位置和右侧一个位置的字符都相同,则该字符串是优美的。
给出字符串 \(s\),问要让串是优美的最少删除几个字符。
数据范围: \(1 \leq |s| \leq 2 \times 10^ 5\)。
题解
删除最少字符可以转化为保留最多字符。
考虑动态规划。
设 \(f[i][j][0/1]\) 表示前 \(i\) 个字符,最后一个选择的字符为 \(j\) ,当前最后选择的字符作为新产生字符串的奇数位 \(1\) 还是偶数位 \(0\)。
转移:
- \(f[i][j][0 / 1] = f[i - 1][j][0 / 1]\)。
- \(f[i][j][0] = f[i - 1][j][1] + 1\)。
- \(f[i][j][1] = \max\{f[i - 1][k][0] + 1\}\)。
上述的 \(k\) 那一维可以独立处理。
时间复杂度 \(O(26n)\)。
C++ 代码示例
# include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
const int inf = 1e9;
int f[N][27][2];
int main() {
int T; cin >> T;
while (T--) {
string s; cin >> s; int n = s.length();
for (int i = 0; i < 26; i++) f[0][i][0] = 0, f[0][i][1] = -inf;
for (int i = 1; i <= n; i++) {
int res = -inf;
for (int j = 0; j < 26; j++)
res = max(res, f[i - 1][j][0] + 1);
for (int j = 0; j < 26; j++) {
f[i][j][0] = f[i - 1][j][0];
f[i][j][1] = f[i - 1][j][1];
f[i][s[i - 1] - 'a'][0] = max(f[i][s[i - 1] - 'a'][0], f[i - 1][s[i - 1] - 'a'][1] + 1);
f[i][s[i - 1] - 'a'][1] = max(f[i][s[i - 1] - 'a'][1], res);
}
}
int ans = 0;
for (int i = 0; i < 26; i++)
ans = max(ans, f[n][i][0]);
cout << n - ans << endl;
}
return 0;
}
D - Maximum Product Strikes Back
题意
长度为 \(n\) 的整数序列 \(a_i(|a_i|\leq 2)\), 求积最大的连续子序列。
数据范围:\(1 \leq n \leq 2\times 10^5\)。
题解
考虑贪心。
将元素全部删光,生成空字符串,积为 \(1\),所以答案一定大于等于 \(1\)。
如果选取的连续子序列中包含至少一个 \(0\),那么积为 \(0\) 必然不是最优。
因此考虑将原序列按照 \(0\) 的位置分成极大串。对于每一个串,依次处理。
- 如果当前字串乘积为正数,那么不用删除数。
- 如果当前串乘积为负数,那么需要删除一个负数。
显然删除在当前序列的最左边第一个负数,或者删除当前序列的最右边第一个负数,剩余的数乘积最大。
由于最大值一定是 \(2\) 的幂次,所以我们只需要关心 \(2\) 在序列中出现几次,就可以比较不同子序列的大小。
时间复杂度为 \(O(n)\)。
C++ 代码示例
# include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
const int inf = 1e9;
int n, a[N];
int main() {
int t; cin >> t;
while (t--) {
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
vector<pair<int, int>>v;
for (int i = 1; i <= n; i++) if (a[i] != 0) {
int j = i + 1; while (j <= n && a[j] != 0) j++; j--;
v.push_back({i, j});
i = j;
}
int ans = -inf, ansl = -inf, ansr = -inf;
for (auto tmp : v) {
int l = tmp.first, r = tmp.second;
int cnt1 = 0, cnt2 = 0;
for (int i = l; i <= r; i++) {
if (a[i] < 0) cnt1++;
if (abs(a[i]) == 2) cnt2++;
}
if (cnt1 % 2 == 0) {
if (cnt2 > ans) ansl = l, ansr = r, ans = cnt2;
continue;
}
int tm = 0;
for (int i = l; i <= r; i++) {
if (abs(a[i]) == 2) tm++;
if (a[i] < 0) {
if (cnt2 - tm > ans) ansl = i + 1, ansr = r, ans = cnt2 - tm;
break;
}
}
tm = 0;
for (int i = r; i >= l; i--) {
if (abs(a[i]) == 2) tm++;
if (a[i] < 0) {
if (cnt2 - tm > ans) ansl = l, ansr = i - 1, ans = cnt2 - tm;
break;
}
}
}
if (ans < 0) cout << "0 " << n << endl;
else cout << ansl - 1 << " " << n - ansr << endl;
}
return 0;
}
E - Matrix and Shifts
题意
一个 \(n \times n\) 的 \(01\)矩阵,可以做如下 \(4\) 种免费的操作:
a. 将行循环上移,b. 将行循环下移,c. 将列循环左移,d. 将列循环右移
可以做如下代价为 \(1\) 的操作:将\(a[x][y]\) 异或 \(1\)。
问将矩阵变成单位对角矩阵的最小代价。
数据范围:\(1 \leq n \leq 2000\)。
题解
如果我们把行扩展两倍,列扩展两倍,那么免费变化的矩形,是\(2n \times 2n\)矩形中一个\(n\times n\)的子矩形。
为了计算通过免费操作,最多能把多少个 \(1\) 放在对角线上,我们可以处理这个\(2n \times 2n\)矩形的对角线。
求出每条对角线上连续长度为 \(n\) 的 \(1\) 的个数的最大值。
然后可以计算出最小代价。
时间复杂度为 \(O(n^2)\)
C++ 代码示例
# include <bits/stdc++.h>
using namespace std;
const int N = 2e3 + 10;
int a[N << 1][N << 1];
vector<int>v[N * N];
int main() {
int t; cin >> t;
while (t--) {
int n; cin >> n;
for (int i = 1; i <= n; i++) {
string s; cin >> s;
for (int j = 0 ; j < n; j++)
a[i][j + 1] = s[j] - '0';
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++) {
a[i + n][j] = a[i][j + n] = a[i + n][j + n] = a[i][j];
}
int tot = 0;
for (int i = 1; i <= 2 * n; i++) {
int x = 1, y = i;
tot++; v[tot].clear();
while (x <= 2 * n && y <= 2 * n) {
v[tot].push_back(a[x++][y++]);
}
}
for (int i = 1; i <= 2 * n; i++) {
int x = i, y = 1;
tot++; v[tot].clear();
while (x <= 2 * n && y <= 2 * n) {
v[tot].push_back(a[x++][y++]);
}
}
int ans = 0;
for (int i = 1; i <= tot; i++) if (v[i].size() >= n) {
int cnt = 0;
for (int j = 0; j < n; j++) cnt += v[i][j];
ans = max(ans, cnt);
for (int j = n; j < v[i].size(); j++) {
if (v[i][j - n] == 1) cnt--;
if (v[i][j] == 1) cnt++;
ans = max(ans, cnt);
}
}
int cnt = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
if (a[i][j] == 1) cnt++;
cout << n - ans + cnt - ans << endl;
}
return 0;
}
F - Promising String
题意
一个字符串被称为好的,当且仅当经过若干次下列操作使得其中\(+,-\)字符个数相等。
操作:将两个连续的 \(-\) 变成 \(+\)
求出长度为 \(n\) 的字符串 \(s\) 有多少连续字串是好的。
easy version : \(1 \leq n \leq 3000\)。
hasrd version : \(1 \leq n \leq 2\times 10^5\)。
题解
观察到下列性质:
一个字符串是好的,当且仅当 \(+\) 的个数比 \(-\) 个数少,且差值为 \(3\) 的倍数。
-
easy version 暴力 \(O(n^2)\)
-
hard version 树状数组 \(O(n \log_2 n)\)
预处理前缀 \(+\) 的个数减去 \(-\) 的差值 的前缀和 \(s[i]\)。
那么一个连续子序列可以表示为 \(s[j] - s[i]\)。
需要满足 \(s[i] \leq s[j]\) 并且 \(s[i] \ \text{mod} \ 3 = s[j] \ \text{mod}\ 3\)。
可以建 \(3\) 个树状数组,维护模 \(3\) 余数的 \(s[j]\) 值域信息即可。
C++ 代码示例
F1.cpp
# include <bits/stdc++.h>
using namespace std;
const int N = 3e3 + 10;
int a[N];
int main() {
int t; cin >> t;
while (t--) {
int n; cin >> n;
string s; cin >> s;
for (int i = 1; i <= n; i++) a[i] = ((s[i - 1] == '+') ? -1 : 1);
int ans = 0;
for (int i = 1; i <= n; i++) {
int cnt = 0;
for (int j = i; j <= n; j++) {
cnt += a[j];
if (cnt >= 0 && cnt % 3 == 0) ans++;
}
}
cout << ans << endl;
}
return 0;
}
F2.cpp
# include <bits/stdc++.h>
# define int long long
# define lowbit(x) (x&(-x))
using namespace std;
const int N = 2e5 + 10;
int a[N], s[N], n;
struct BST {
int c[N << 1];
void update(int x, int d) {
x += n + 1;
for (;x <= 2 * n; x += lowbit(x)) c[x] += d;
}
int query(int x) {
x += n + 1;
int res = 0;
for (; x ; x -= lowbit(x)) res += c[x];
return res;
}
}tr[3];
signed main() {
int t; cin >> t;
while (t--) {
cin >> n;
for (int i = 0; i < 3; i++)
for (int j = -n; j <= n; j++)
tr[i].c[j + n + 1] = 0;
string th; cin >> th;
for (int i = 1; i <= n; i++) {
a[i] = ((th[i - 1] == '+') ? -1 : 1);
s[i] = s[i - 1] + a[i];
}
int ans = 0;
tr[0].update(0, 1);
for (int i = 1; i <= n; i++) {
ans += tr[(s[i] % 3 + 3) % 3].query(s[i]);
tr[(s[i] % 3 + 3) % 3].update(s[i], 1);
}
cout << ans << endl;
}
return 0;
}