当前补到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
值和长度为3
的hash
值。)然后去对应长度的set
里面去查其反串的hash
值是否存在。对于长度为2
的串还需要在表示长度为3
的hash
值的set
中去查找,如ab
和cba
。对于长度为3
的串需要去表示长度为2
的hash
值的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;
}