比赛链接

A

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long; bool solve() {
int n;
cin >> n;
if (n <= 4) cout << "Bob" << '\n';
else cout << "Alice" << '\n';
return true;
} int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--) {
if (!solve()) cout << -1 << '\n';
}
return 0;
}

B

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long; bool solve() {
int q;
cin >> q; bool ok = 1;
int limit = -1;
int pre = 0;
while (q--) {
int x;
cin >> x;
if (limit < 0) limit = x;
if (ok) {
if (pre <= x || x <= limit) {
if (pre > x) ok = 0;
cout << 1;
pre = x;
}
else cout << 0;
}
else {
if (pre <= x && x <= limit) cout << 1, pre = x;
else cout << 0;
}
}
cout << '\n';
return true;
} int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--) {
if (!solve()) cout << -1 << '\n';
}
return 0;
}

C

题目

给定一个字符串 \(s\) ,仅含有 A-E \(5\) 个大写字母,其中字母代表一个值 |A| = 1, ..., |E| = 10000

一个字符串的值为 \(\displaystyle \sum_{i=1}^n (-1)^{\left[s_i < \max\limits_{i+1 \leq j \leq n}(s_i)\right]} \cdot |s_i|\) 。

题解

方法一

知识点:线性dp,枚举。

考虑枚举每个位置作为修改的位置。

对于位置 \(i\) 的修改,只会影响到 \([1,i-1]\) 之间字符值的符号,而 \([i+1,n]\) 是不会被影响的。因此,我们需要预先知道, \([1,i-1]\) 在 \([i,n]\) 最大值确定时的值。

设 \(f_{i,j}\) 表示 \([1,i-1]\) 在 \([i,n]\) 的最大字符为 \(j\) 时的值。转移方程为:

\[f_{i,j} = f_{i-1,\max(s_{i-1},j)} + (-1)^{[s_{i-1}<j]} \cdot |s_{i-1}|
\]

随后,我们从后往前枚举修改位置 \(i\) 方便递推。设 \([i+1,n]\) 部分的值 \(sub\) 以及最大字符 \(mx\) ,若 \(s_i\) 修改为 \(j\) ,答案则为:

\[f_{i,\max(mx, j)} + (-1)^{[j < mx]} \cdot |j| + sub
\]

然后,我们递推 \(sub = sub + (-1)^{[s_i < mx]} \cdot |s_i|\) ,然后 \(mx = \max(mx,s_i)\) 。

最后,取 \(n\) 个修改位置的最大值即可。

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

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

方法二

知识点:线性dp。

由于修改 \(i\) 时,\([i+1,n]\) 是不会被影响的,考虑从右往左递推。

设 \(f_{i,j,k}\) 表示 \([i,n]\) 的最大字符是 \(j\) 且修改了 \(k\) 次的最大值。转移方程为:

\[f_{i,\max(j,y),k + [s_{i} \neq y]} = f_{i+1,j,k} + (-1)^{[y<\max(j,y)]} \cdot |y|
\]

其中 \(y\) 是 \(s_{i}\) 的字符, \(k+[s_{i} \neq y]\) 应该小于 \(2\) 保证修改次数小于 \(2\) 。

这种方法比方法一更加通用好写。

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

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

代码

方法一

#include <bits/stdc++.h>
using namespace std;
using ll = long long; int f[200007][5];//[1,i-1]在[i,n]的最大值为j时的值
const int mp[5] = { 1,10,100,1000,10000 };
bool solve() {
string s;
cin >> s;
int n = s.size();
s = "?" + s; for (int i = 2;i <= n;i++) {
int x = s[i - 1] - 'A';
for (int j = 0;j < 5;j++) f[i][j] = f[i - 1][max(j, x)] + (x < j ? -1 : 1) * mp[x];
} int ans = -2e9;
int sub = 0, mx = 0;
for (int i = n;i >= 1;i--) {
for (int j = 0;j < 5;j++) ans = max(ans, f[i][max(mx, j)] + (j < mx ? -1 : 1) * mp[j] + sub);
int x = s[i] - 'A';
sub += (x < mx ? -1 : 1) * mp[x];
mx = max(x, mx);
} cout << ans << '\n';
return true;
} int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--) {
if (!solve()) cout << -1 << '\n';
}
return 0;
}

方法二

#include <bits/stdc++.h>
using namespace std;
using ll = long long; int f[2][5][2];//[i,n]的最大值为j且修改了k次时的值
const int mp[5] = { 1,10,100,1000,10000 };
bool solve() {
string s;
cin >> s;
int n = s.size();
s = "?" + s; for (int j = 0;j < 5;j++) f[0][j][0] = f[0][j][1] = -1e9;
f[0][0][0] = 0;
for (int i = n;i >= 1;i--) {
for (int j = 0;j < 5;j++) f[1][j][0] = f[1][j][1] = -1e9; int x = s[i] - 'A';
for (int j = 0;j < 5;j++) {
for (int k = 0;k < 2;k++) {
for (int y = 0;y < 5;y++) {
int jj = max(j, y);
int kk = k + (x != y); // k = 1,x != y,之前修改了这次也修改了,所以不合法
if (kk < 2) f[1][jj][kk] = max(f[1][jj][kk], f[0][j][k] + (y < jj ? -1 : 1) * mp[y]);
}
}
} swap(f[0], f[1]);
} int ans = -1e9;
for (int j = 0;j < 5;j++) ans = max({ ans,f[0][j][0],f[0][j][1] });
cout << ans << '\n';
return true;
} int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--) {
if (!solve()) cout << -1 << '\n';
}
return 0;
}

D

题目

有 \(k\) 个线段, \(k\) 是偶数,如果能把他们分成两个一组,且满足要求:

  1. 每个线段只能出现在一个组里。
  2. 同一组的线段必须相交。
  3. 不同组的线段不能相交。

那么称这 \(k\) 个线段是合格的。

现在给你 \(n\) 个线段,问最少需要去掉多少条线段,才能使得剩下的线段是合格的。

题解

知识点:贪心,枚举。

问题等价于最多能分多少组,就是活动安排问题的升级版,思路还是一样的。

先按右端点排序,贪心地从左往右选,是为了尽可能让后面选的线段不会与前面已经分好的组碰撞。我们用 \(maxr\) 记录已经分好组的线段的最大右端点,之后左端点小于等于它的都是不能选的。

我们还需要让同一组的线段相交,所以需要记录上一个选好的线段的右端点 \(lastr\) 。新的线段在大于 \(maxr\) 的基础上,若小于等于 \(lastr\) ,那么就可以与上一个选的线段直接贪心地合并成一组,这样保证新的组右端点最小,同样是为了尽可能让后面选的线段不会与前面已经分好的组碰撞,然后答案加 \(1\) 并且更新 \(maxr\) ;否则,用新的线段的右端点更新 \(lastr\) ,使得后面更有机会和这个线段合并。

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

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

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long; int l[2007], r[2007];
int ord[2007]; bool solve() {
int n;
cin >> n;
for (int i = 1;i <= n;i++) cin >> l[i] >> r[i];
iota(ord + 1, ord + n + 1, 1);
sort(ord + 1, ord + n + 1, [&](int a, int b) {return r[a] < r[b];}); int ans = 0, maxr = -1, lastr = -1;
for (int i = 1;i <= n;i++) {
int id = ord[i];
if (l[id] <= maxr) continue;
if (l[id] <= lastr) {
ans++;
maxr = r[id];
}
else lastr = r[id];
}
cout << n - 2 * ans << '\n';
return true;
} int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--) {
if (!solve()) cout << -1 << '\n';
}
return 0;
}

E

题目

给一个 \(n\times n\) 的矩阵,第 \(i\) 列 \([1,a_i]\) 的格子是黑的而 \([a_i+1,n]\) 的格子是白的。

有 \(1, \cdots ,m\) 的 \(m\) 个数字,要填在矩阵中,每个数字只能填一次,只能填在白色的格子里。

定义一个矩阵的值为数字 \(j\) 的数量,其中 \(j\) 满足 \(j+1\) 填在同一行下一列。

求矩阵的最大值。

题解

方法一

知识点:单调栈,贪心,枚举。

模拟一下发现,每行白色格子被分成了若干段,要将数字填在尽量长的白色连续段内,因为一个白色连续段只会损失一个数字。因此,问题变成如何求出各种长度的白色连续段有多少个。

对于一个白色连续段的长度,其实就是它所在的位置能左右扩展直到遇到黑色格子的长度,即左右第一个白色列高小于自己的列高的位置。同时,我们发现一组相同位置和长度,但高度不同的连续白色段,一定是上下相邻的,且最大高度一定是跨越的白色列的高度最小值,最小高度一定是左右边界高度的最大值。

因此,考虑求出每个白色列以自己最大高度能扩展的左右边界,就是以这列高度为最大高度的白色连续段边界。这是个经典问题,可以用单调栈解决左右扩展边界的问题。

随后,我们就可以通过每个白色列的左右边界求出白色连续段的长度,列高减去左右边界的最大高度就是以这一列高度为最大高度往下的连续白色段的行数,即段数。

为了防止相同高度的白色列重复计算同一组白色连续段,考虑将左边界的意义改为第一个白色列高小于等于自己的列。这样只有最左侧的一列才会计算一次,其他列会因为左边界的高度等于自己的高度,从而计算出的段数为 \(0\) 。

得到了各种长度的白色连续段数,就可以贪心填数字了,注意数字不够填可以填段的部分。

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

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

方法二

知识点:STL,贪心,枚举。

注意到,白色连续段是从低到高,即行从大到小,逐渐被黑色格子分段的。

我们可以用 set 模拟分割过程,从低到高加入分段点,找到被分段的白色连续段端点可以计算出长度,再利用当前高度减去左右边界的最大高度,即左右边界的行最小值,就可以求出段数。

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

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

代码

方法一

#include <bits/stdc++.h>
using namespace std;
using ll = long long; int a[200007];
int l[200007], r[200007];
ll cnt[200007];
bool solve() {
int n;
cin >> n;
for (int i = 1;i <= n;i++) {
cin >> a[i];
a[i] = n - a[i];
l[i] = 0, r[i] = n + 1;
cnt[i] = 0;
}
ll m;
cin >> m; vector<int> stk;
for (int i = 1;i <= n;i++) {
while (stk.size() && a[stk.back()] > a[i]) {
r[stk.back()] = i;
stk.pop_back();
}// 右开边界是第一个小于
if (stk.size()) l[i] = stk.back();// 左开边界是第一个小于等于
stk.push_back(i);
}
//! 左侧第一个小于等于,会让左侧第一个相同高度的有正确的左边界,其他的会在左侧等于的地方停止,避免之后重复计数
//! 右侧第一个小于,是因为要让左侧第一个相同高度有正确的右边界,其他的虽然也会到正确的右边界,但是会因为左边界高度被排除
//! 也可以左侧小于,右侧小于等于 for (int i = 1;i <= n;i++) {
int len = r[i] - l[i] - 1;
int h = 0;
if (l[i] >= 1) h = max(h, a[l[i]]);//! 相同的高度,但不是左侧第一个,h会等于a[i]
if (r[i] <= n) h = max(h, a[r[i]]);
cnt[len] += a[i] - h;
} ll rest = m;
ll ans = m;
for (int i = n;i >= 1;i--) {
if (i * cnt[i] < rest) {
ans -= cnt[i];
rest -= cnt[i] * i;
}
else {
ans -= (rest + i - 1) / i;
rest = 0;
}
}
cout << ans << '\n';
return true;
} int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--) {
if (!solve()) cout << -1 << '\n';
}
return 0;
}

方法二

#include <bits/stdc++.h>
using namespace std;
using ll = long long; int a[200007];
vector<int> pos[200007];
ll cnt[200007];
bool solve() {
int n;
cin >> n;
for (int i = 0;i <= n;i++) pos[i].clear();
for (int i = 1;i <= n;i++) {
cin >> a[i];
pos[a[i]].push_back(i);
cnt[i] = 0;
}
ll m;
cin >> m; set<int> st;
st.insert(0);
st.insert(n + 1);
a[0] = a[n + 1] = n;
for (int i = n;i >= 0;i--) {
for (auto x : pos[i]) {
auto it = st.lower_bound(x);
int pre = *prev(it);
int nxt = *it;
cnt[nxt - pre - 1] += min(a[pre], a[nxt]) - i;
st.insert(x);
}
} ll rest = m;
ll ans = m;
for (int i = n;i >= 1;i--) {
if (i * cnt[i] < rest) {
ans -= cnt[i];
rest -= cnt[i] * i;
}
else {
ans -= (rest + i - 1) / i;
rest = 0;
}
}
cout << ans << '\n';
return true;
} int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--) {
if (!solve()) cout << -1 << '\n';
}
return 0;
}

Educational Codeforces Round 150 (Rated for Div. 2) A-E的更多相关文章

  1. Educational Codeforces Round 60 (Rated for Div. 2) - C. Magic Ship

    Problem   Educational Codeforces Round 60 (Rated for Div. 2) - C. Magic Ship Time Limit: 2000 mSec P ...

  2. Educational Codeforces Round 60 (Rated for Div. 2) - D. Magic Gems(动态规划+矩阵快速幂)

    Problem   Educational Codeforces Round 60 (Rated for Div. 2) - D. Magic Gems Time Limit: 3000 mSec P ...

  3. Educational Codeforces Round 43 (Rated for Div. 2)

    Educational Codeforces Round 43 (Rated for Div. 2) https://codeforces.com/contest/976 A #include< ...

  4. Educational Codeforces Round 35 (Rated for Div. 2)

    Educational Codeforces Round 35 (Rated for Div. 2) https://codeforces.com/contest/911 A 模拟 #include& ...

  5. Codeforces Educational Codeforces Round 44 (Rated for Div. 2) F. Isomorphic Strings

    Codeforces Educational Codeforces Round 44 (Rated for Div. 2) F. Isomorphic Strings 题目连接: http://cod ...

  6. Codeforces Educational Codeforces Round 44 (Rated for Div. 2) E. Pencils and Boxes

    Codeforces Educational Codeforces Round 44 (Rated for Div. 2) E. Pencils and Boxes 题目连接: http://code ...

  7. Educational Codeforces Round 63 (Rated for Div. 2) 题解

    Educational Codeforces Round 63 (Rated for Div. 2)题解 题目链接 A. Reverse a Substring 给出一个字符串,现在可以对这个字符串进 ...

  8. Educational Codeforces Round 39 (Rated for Div. 2) G

    Educational Codeforces Round 39 (Rated for Div. 2) G 题意: 给一个序列\(a_i(1 <= a_i <= 10^{9}),2 < ...

  9. Educational Codeforces Round 48 (Rated for Div. 2) CD题解

    Educational Codeforces Round 48 (Rated for Div. 2) C. Vasya And The Mushrooms 题目链接:https://codeforce ...

  10. Educational Codeforces Round 60 (Rated for Div. 2) 题解

    Educational Codeforces Round 60 (Rated for Div. 2) 题目链接:https://codeforces.com/contest/1117 A. Best ...

随机推荐

  1. 图与网络分析—R实现(四)

    三 最短路问题 最短路问题(short-path problem)是图论理论的一个经典问题.寻找最短路径就是在指定网络中两结点间找一条距离最小的路.最短路不仅仅指一般地理意义上的距离最短,还可以引申到 ...

  2. vue指令之事件指令

    目录 什么是事件指令 示例 什么是事件指令 事件指的是:点击事件,双击事件,划动事件,焦点事件... 语法 v-on:事件名='函数' # 注意:函数必须写在 methods配置项中 示例 # 点击按 ...

  3. 华为 A800-9000 服务器 离线安装MindX DL

    MindX DL(昇腾深度学习组件)是支持 Atlas 800 训练服务器.Atlas 800 推理服务器的深度学习组件参考设计,提供昇腾 AI 处理器资源管理和监控.昇腾 AI 处理器优化调度.分布 ...

  4. LeeCode 318周赛复盘

    T1: 对数组执行操作 思路:模拟 public int[] applyOperations(int[] nums) { int n = nums.length; for (int i = 0; i ...

  5. if elseif else 怎么用?

    问题:求三个数中的最大值 上代码-- 第一种  两两比较  每次取较大值 和第三个值比较  最终得到最大值 private static void maxIf2() { int a = (int) ( ...

  6. 注解:@RequiredArgsConstructor、 @Validated、 @Valid、 @Lazy

    1. lombok注解:@RequiredArgsConstructor Spring 依赖注入方式 1.通过 @Autowire.@Resource 等注解注入, 2.通过构造器的方式进行依赖注入. ...

  7. YOLO3论文中文版

    文章目录 YOLO3论文中文版 摘要 1.引言 2. 解决方案 2.1 边界框预测 2.2 类预测 2.3 多尺度预测 2.4 特征提取器 2.5 训练 3.我们的做法 4. 失败的尝试 5.这一切意 ...

  8. Redis分布式锁实现及使用

    文章目录 分布式锁 全局ID生成器 一人一单实现 超卖问题 一人一单 分布式锁 Redis setnx实现分布式锁 Redis在业内解决秒杀等业务场景有非常广的应用,如何设计实现一个分布式锁是解决超卖 ...

  9. 几种常见的Python数据结构

    摘要:本文主要为大家讲解在Python开发中常见的几种数据结构. 本文分享自华为云社区<Python的常见数据结构>,作者: timerring . 数据结构和序列 元组 元组是一个固定长 ...

  10. [python] Python类型提示总结

    Python3.5 版本引入了类型提示(Type Hints),它允许开发者在代码中显式地声明变量.函数.方法等的类型信息.这种类型声明不会影响 Python 解释器的运行,但可以让 IDE 和静态分 ...