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。设 dp_j 为当前子序列状态为 j 的最短前缀长度,j 是一个表示是否包含数字 i 的二进制数 (1\le i\le 20)。对于数字 i,只需要从 j \oplus (1 << (i - 1)) 转移到当前位置即可。

#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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值