Codeforces Round #767 (Div. 2) 题解

当前补到D

A. Download More RAM

题目描述:给你两个长度为\(n\)的数组\(a , b\),再给你一个初始值\(k\),你可以使用数组\(b\)增加\(k\)的值,但前提是你当前的\(k\)要大于等于将要使用的数组\(b\)对应元素的下标,问\(k\)最大能到多少。

思路:比较明显的贪心,将两个数组捆绑然后按照\(a\)从小到大排序,然后模拟即可。

时间复杂度:\(O(nlogn)\)

参考代码:

int n, k;
void solve() {
	cin >> n >> k;
	vector<vector<int>>a(n, vector<int>(2, 0));
	for (int i = 0; i < n; ++i) cin >> a[i][0];
	for (int i = 0; i < n; ++i) cin >> a[i][1];
	sort(a.begin(), a.end(), [&](const vector<int>& b, const vector<int>& c) {
		return b[0] < c[0];
		});
	for (int i = 0; i < n; ++i) {
		if (a[i][0] > k) break;
		else k += a[i][1];
	}
	cout << k << '\n';
	return;
}

B. GCD Arrays

题目描述:给定一个排列a,元素范围为[lr , rs],再给你一个整数k,你可以执行下列操作不超过k次:

  • a中取出两个元素
  • 将两数相乘
  • 将乘积插入原数组的末尾

问是否能通过上述操作使得数组的最大公约数大于1

思路:考虑到奇数乘偶数就可以变成偶数,偶数的最大公约数至少为2,故可以统计区间内奇数的个数,如果超过k,就不能达成目的,否则能;需要特判长度为1的奇数序列。

时间复杂度:\(O(T)\)

参考代码:

int lr, rs, k;
void solve() {
	cin >> lr >> rs >> k;
	int len = rs - lr + 1;
	if (len == 1 && lr > 1) {
		cout << "YES" << '\n';
		return;
	}
	int cnt = len >> 1;
	if (len % 2 == 1 && lr % 2 == 1) ++cnt;
	if (cnt > k) cout << "NO" << '\n';
	else cout << "YES" << '\n';
	return;
}

C. Meximum Array

题目描述:给你一个数组\(a\),每次你可以选择\(a\)的一个长度为\(k\)的前缀,并将其前缀的MEX加入数组\(b\)中,然后将该前缀从数组\(a\)中删去,重复操作直到\(a\)为空。求字典序最大的\(b\)数组。

思路:比较明显的贪心,我们可以使用桶记录一下数组\(a\)中每个数字出现的次数,然后从头到尾遍历,如果MEX在后面没有再出现,那就将当前的MEX加入\(b\)数组然后重置MEX。重复上述操作直到数组\(a\)为空即可。

时间复杂度:\(O(n)\)

空间复杂度:\(O(n)\)

参考代码:

 
int n;
const int N = 2e5 + 5;
int cnt[N];
bool vis[N];
void solve() {
	cin >> n;
	vector<int>a(n, 0);
	for (int i = 0; i < n; ++i) cin >> a[i], ++cnt[a[i]];
	vector<int> res;
	int mex = 0, last = 0;
	for (int i = 0; i < n; ++i) {
		vis[a[i]] = true;
		while (vis[mex] == true) ++mex;
		--cnt[a[i]];
		if (cnt[mex] == 0) {
			res.push_back(mex); mex = 0;
			for (int j = last; j <= i; ++j) vis[a[j]] = false;
			last = i + 1;
		}
	}
	cout << res.size() << '\n';
	for (auto re : res) cout << re << " ";
	cout << '\n';
	return;
}

D. Peculiar Movie Preferences

题目描述:给你\(n\)个长度不超过3的字符串,如果你能从中选出一些字符串,并按照给定的顺序将其拼起来组成新的字符串,该字符串是回文就输出YES,否则输出NO

思路:比较容易想到的是若存在回文串,则一定存在长度不超过6的回文串。我们遍历所有给定的字符串,对于当前枚举的字符串,先判断自己本身是否是回文。(我们用两个set存储字符串的hash值,分别是长度为2的字符串的hash值和长度为3hash值。)然后去对应长度的set里面去查其反串的hash值是否存在。对于长度为2的串还需要在表示长度为3hash值的set中去查找,如abcba。对于长度为3的串需要去表示长度为2hash值的set里面去查找其长度为2的后缀的反串,例子如前。若都没找到,先将串的hash值存入对应的set中,对于长度为3的串,还需要存储其长度为2的前缀的hash值到set3中。由于当前字符串的长度很小,hash值范围不大,也可以使用静态数组存储进行优化。

时间复杂度:\(O(nlogn)\)

参考代码:

int n;
//hash
auto cal = [](string& s)->int {
	int dx = 0;
	for (auto c : s) dx = dx * 27 + (c - 'a' + 1);
	return dx;
};
void solve() {
	cin >> n;
	vector<string>strs(n);
	for (int i = 0; i < n; ++i) cin >> strs[i];
	set<int>s[4];
	for (auto str : strs) {
		string t = str;
		reverse(t.begin(), t.end());
		//在长度相同的里面去查找反串是否存在
		if (t == str || s[str.size()].find(cal(t)) != s[str.size()].end()) {
			cout << "YES" << '\n';
			return;
		}
		//如果长度为2 要去长度为3里面查找长度为2的前缀和是否存在
		if (str.size() == 2 && s[3].find(cal(t)) != s[3].end()) {
			cout << "YES" << '\n';
			return;
		}
		s[str.size()].insert(cal(str));
		if(str.size() == 3) {
			//检验长度为3的后缀的反串是否在前面出现
			t = str.substr(1, 2);
			reverse(t.begin(), t.end());
			if (s[2].find(cal(t)) != s[2].end()) {
				cout << "YES" << endl;
				return;
			}
			//将前缀的hash值加入set
			t = str.substr(0, 2);
			s[3].insert(cal(t));
		}
	}
	cout << "NO" << endl;
	return;
}
上一篇:[Acwing Contest] 第 11 场周赛 题解


下一篇:搜索题锦——BFS