Atcoder Beginner Contest 381
比赛链接:AtCoder Beginner Contest 381 - AtCoder
A - 11/22 String
#include <bits/stdc++.h>
using namespace std;
signed main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int n; string s;
cin >> n >> s;
if (n & 1) {
for (int i = 0; i < n / 2; i++) if (s[i] != '1') { cout << "No" << endl; return 0; }
if (s[n / 2] != '/') { cout << "No" << endl; return 0; }
for (int i = n / 2 + 1; i < n; i++) if (s[i] != '2') { cout << "No" << endl; return 0; }
cout << "Yes" << endl;
return 0;
}
cout << "No" << endl;
return 0;
}
B - 1122 String
#include <bits/stdc++.h>
using namespace std;
signed main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int n; string s;
cin >> s;
n = s.size();
map<char, int> mp;
if (n & 1) { cout << "No" << endl; return 0; }
for (int i = 1; i < n; i += 2) {
if (s[i] != s[i - 1]) {
cout << "No" << endl;
return 0;
}
if (mp.find(s[i]) != mp.end()) {
cout << "No" << endl;
return 0;
}
mp[s[i]] = 2;
}
cout << "Yes" << endl;
return 0;
}
C - 11/22 Substring
遍历字符串,当当前字符为 '/' 的时候,分别向左边找连续 1,向右边找连续 2 的个数,当前合法的 11/22 串长度为两者最小值的两倍加 1。
#include <bits/stdc++.h>
using namespace std;
signed main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int n; string s;
cin >> n >> s;
int ans = 0;
for (int i = 0; i < n; i++) {
if (s[i] == '/') {
int l = i, r = i;
while (l - 1 >= 0 && s[l - 1] == '1') l--;
while (r + 1 < n && s[r + 1] == '2') r++;
int res = min(r - i, i - l);
ans = max(ans, 2 * res + 1);
}
}
cout << ans << endl;
return 0;
}
D - 1122 Substring
根据题意利用双指针来做。
如果接下来两个数相同,
- 该数在前面子串没有出现,将右端指针往后移两位。
- 该数在前面子串已经出现,记录答案,左端指针右移至该数的位置, 再将右端指针往后移两位。
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int n, a[N];
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
int l = 1, ans = 0;
map<int, int> cnt; // 记录数字在连续子串中是否出现
for (int r = 1; r <= n; r++) {
if ((r > 1 && a[r] == a[r - 1]) || (r - l) % 2 == 0) {
cnt[a[r]]++;
while (cnt[a[r]] > 2) {
cnt[a[l]]--;
if (cnt[a[l]] == 0) cnt.erase(a[l]);
l++;
}
if ((r - l + 1) % 2 == 0) {
ans = max(ans, r - l + 1);
}
}
else {
cnt.clear();
l = r;
cnt[a[r]] = 1;
}
}
cout << ans << endl;
return 0;
}
E - 11/22 Subsequence
记录字符串前缀中 1 和 2 的数量以及字符 '/' 的位置。对于每次查询,利用二分找出所有在区间内的 '/' 的下标范围 [ L, R ],再在这个区间内利用二分找到最长的子序列长度。
子序列长度等于 '/' 左侧 1 的数量和右侧 2 的数量的最小值的两倍加 1。
- 左侧的 1 数量大于右侧 2 的数量时,最长子序列对应的 '/' 在右边。
- 左侧的 1 数量小于右侧 2 的数量时,最长子序列对应的 '/' 在左边。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n, q, a[N], b[N];
vector<int> v;
string s;
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> n >> q >> s;
int len = s.size();
s = " " + s;
for (int i = 1; i <= len; i++) {
a[i] = a[i - 1] + (s[i] == '1');
b[i] = b[i - 1] + (s[i] == '2');
if (s[i] == '/') v.push_back(i);
}
while (q--) {
int l, r, ans = 0; cin >> l >> r;
int L = lower_bound(v.begin(), v.end(), l) - v.begin();
int R = upper_bound(v.begin(), v.end(), r) - v.begin() - 1;
while (L <= R) {
int mid = L + R >> 1;
int c1 = a[v[mid] - 1] - a[l - 1], c2 = b[r] - b[v[mid]];
ans = max(ans, 2 * min(c1, c2) + 1);
if (c1 < c2) L = mid + 1;
else R = mid - 1;
}
cout << ans << '\n';
}
return 0;
}
F - 1122 Subsequence
状压DP。设 为当前子序列状态为 j 的最短前缀长度,j 是一个表示是否包含数字 i 的二进制数 ()。对于数字 i,只需要从 转移到当前位置即可。
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int n; cin >> n;
vector<int> a(n + 1), dp(1 << 20 + 5, INT_MAX);
vector<vector<int>> pos(21);
for (int i = 1; i <= n; i++) {
cin >> a[i];
pos[a[i]].push_back(i); // 记录 a[i] 出现的位置
}
dp[0] = 0;
int ans = 0;
for (int j = 1; j < (1 << 20); j++) {
for (int i = 1; i <= 20; i++) {
// j 为当前状态,需要判断 j 状态中 i 对应的位置上是否为 1
// 即 j 状态是否表示已经包含了 i 这个数字
// 同时还需要判断 j ^ (1 << (i - 1)) 这个状态是否存在
if ((j & (1 << (i - 1))) && dp[j ^ (1 << (i - 1))] != INT_MAX) {
int idx = upper_bound(pos[i].begin(), pos[i].end(), dp[j ^ (1 << (i - 1))]) - pos[i].begin();
// 找出第一个在 dp[j ^ (1 << (i - 1))] 后面的 i 的位置
if (idx + 1 < pos[i].size()) dp[j] = min(dp[j], pos[i][idx + 1]);
// 子序列需要插入两个相同的数,所以判断 idx + 1 是否合法
}
}
if (dp[j] != INT_MAX) {
int res = 0, tmp = j;
while (tmp) res += (tmp & 1), tmp >>= 1; // 统计 j 的二进制表示中 1 的个数
ans = max(ans, 2 * res);
}
}
cout << ans << '\n';
return 0;
}