目前补到D
A. Equidistant Letters
题目描述:给你一个字符串\(s\),该字符串只含有小写字母,且每个字母出现次数不超过\(2\)次,让你将\(s\)重排,使得每对出现次数为两次的字符的下标差相同。
思路:将\(s\)排序,使得每对出现次数为两次的字符的下标差都为\(1\)即可。
时间复杂度:\(O(Tnlogn)\),\(n\)为字符串长度。
参考代码:
string s;
void solve() {
cin >> s;
sort(s.begin(), s.end());
cout << s << '\n';
return;
}
B. Minor Reduction
题目描述:给你一个十进制数,长度为\(n\),用字符串表示,你需要选择一个下标\(i , 1 \leq i < n\),然后将\(s_i , s_{i + 1}\)的位置替换成\(s_i + s_{i + 1}\),问这样操作后能取得的最大值。
数据范围:\(1 \leq T \leq 10^4 , 2 \leq n \leq 10^{200000} , \sum n \leq 2 \times 10^ 5\)
思路:先倒着扫一遍,若存在\(s_i + s_{i + 1} \geq 10\)就将该位置变成二者的和,否则将\(s_1 , s_2\)合并。
时间复杂度:\(O(n)\)
string s;
void solve() {
cin >> s;
int n = static_cast<int>(s.size());
for (int i = n - 2; i >= 0; --i) {
int dx = s[i] - '0' + s[i + 1] - '0';
if(dx >= 10) {
s[i] = '1';
s[i + 1] = '0' + (dx % 10);
cout << s << '\n';
return;
}
}
int dx = s[0] - '0' + s[1] - '0';
cout << dx << s.substr(2, n - 2) << '\n';
return;
}
C. Monsters And Spells
题目描述:有\(n\)只怪物,出现在\(k_i\)时刻拥有血条\(h_i\),假设不释放能量是释放了\(0\)的能量,那么每次你可以释放比前一次多一的能量,或者释放\(1\)点能量,只有当释放的能量不小于\(h_i\)时才能杀死怪物,问将所有怪物杀死的最小能量释放数。
数据范围:\(1 \leq T \leq 10^4 , 1 \leq n \leq 100 , 1 \leq k_i \leq 10^9 , 1\leq h_i \leq k_i \leq 10^9 , \sum n \leq 10^4\)
思路:考虑倒着贪心,对于每一个出现在\(k_i\)结点的怪物,他至少需要在\(k_i - h_i + 1\)时刻开始从\(1\)开始递增。故倒着遍历,每次对于当前的\(i\),找到一个\(j\;(j < i)\)使得\(k_j < \mathop{min}\limits_{p = j + 1}^{i} \{k_p-h_p + 1\}\)。记\(need\)表示杀死第\(i\)个怪物最小的开始时间,在上述找寻的过程中需要更新\(need\),其中\(need = \mathop{min}\limits_{p = j + 1}^{i} \{k_p-h_p + 1\}\)
时间复杂度:\(O(\sum n)\)
void solve() {
int n(0);
cin >> n;
vector<int>k(n + 1, 0), h(n + 1, 0);
for (int i = 1; i <= n; ++i) cin >> k[i];
for (int i = 1; i <= n; ++i) cin >> h[i];
long long res = 0;
int need = 0;
int idx = n;
while(idx){
need = k[idx] - h[idx] + 1;
long long dx = k[idx];
--idx;
while (idx && k[idx] >= need) {
int dy = k[idx] - h[idx] + 1;
need = min(need, dy);
--idx;
}
dx = dx - need + 1;
res += dx * (dx + 1) >> 1;
}
cout << res << '\n';
return;
}
D. Martial Arts Tournament
题目描述:给你\(n\)个数字\(a_i\),让你求出两个整数\(x , y\;(x < y)\),将\(a_i\)分成三部分,小于\(x\),大于等于\(y\)和在\([x , y)\)内的。为了让三部分中元素个数满足是二的幂次,你需要添加一些数字,求最少的添加数字的数量。
数据范围:\(1 \leq T \leq 10^4 , 1 \leq n \leq 2 \times 10^5 , 1 \leq a_i \leq n , \sum n \leq 2 \times 10^5\)
思路:考虑到\(a_i \leq n\),可以做一个前缀和优化计算区间内的数字个数,我们枚举\(x\)的位置,然后枚举二的幂次也即\(x + (1 << j)\;(0 \leq j \leq 20)\)作为\(y - 1\),也即第二部分目标含有
1 << j
个元素,二分求\(y - 1\)在前缀和数组中的位置\(pos\)就可以求出三部分各含有多少元素。使用相应的幂次减去各部分含有的就是需要添加的,对所有情况取
min
即可。
时间复杂度:\(O(nlog^2n)\)
const int N = 2e5 + 5;
vector<int> dis(N, 0);
void init() {
int cur = 0;
dis[0] = 1;
for (int i = 1; i < N; ++i) {
if (i > (1 << cur)) ++cur;
dis[i] = 1 << cur;
}
return;
}
void solve() {
int n(0), a(0);
cin >> n;
vector<int>pre(n + 1, 0);
for (int i = 1; i <= n; ++i) cin >> a, pre[a]++;
for (int i = 1; i <= n; ++i) pre[i] += pre[i - 1];
int res = INT_MAX;
for (int i = 0; i <= n; ++i) {
if (i < n && pre[i] == pre[i + 1]) continue;
int ans = dis[pre[i]] - pre[i];
for (int j = 0; j <= 20; ++j) {
int pos = upper_bound(pre.begin(), pre.end(), pre[i] + (1 << j)) - pre.begin() - 1;
res = min(res, ans + (1 << j) + dis[pre[n] - pre[pos]] - (pre[n] - pre[i]));
if (pos == n) break;
}
}
cout << res << '\n';
return;
}