Educational Codeforces Round 150 (Rated for Div. 2) A-E
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\) 时的值。转移方程为:
\]
随后,我们从后往前枚举修改位置 \(i\) 方便递推。设 \([i+1,n]\) 部分的值 \(sub\) 以及最大字符 \(mx\) ,若 \(s_i\) 修改为 \(j\) ,答案则为:
\]
然后,我们递推 \(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\) 次的最大值。转移方程为:
\]
其中 \(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\) 是偶数,如果能把他们分成两个一组,且满足要求:
- 每个线段只能出现在一个组里。
- 同一组的线段必须相交。
- 不同组的线段不能相交。
那么称这 \(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的更多相关文章
- 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 ...
- 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 ...
- Educational Codeforces Round 43 (Rated for Div. 2)
Educational Codeforces Round 43 (Rated for Div. 2) https://codeforces.com/contest/976 A #include< ...
- Educational Codeforces Round 35 (Rated for Div. 2)
Educational Codeforces Round 35 (Rated for Div. 2) https://codeforces.com/contest/911 A 模拟 #include& ...
- 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 ...
- 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 ...
- Educational Codeforces Round 63 (Rated for Div. 2) 题解
Educational Codeforces Round 63 (Rated for Div. 2)题解 题目链接 A. Reverse a Substring 给出一个字符串,现在可以对这个字符串进 ...
- 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 < ...
- Educational Codeforces Round 48 (Rated for Div. 2) CD题解
Educational Codeforces Round 48 (Rated for Div. 2) C. Vasya And The Mushrooms 题目链接:https://codeforce ...
- Educational Codeforces Round 60 (Rated for Div. 2) 题解
Educational Codeforces Round 60 (Rated for Div. 2) 题目链接:https://codeforces.com/contest/1117 A. Best ...
随机推荐
- 一文带你弄懂 Maven 拉包原理
业务需求开发的时候,我们总是会遇到拉不到依赖包的情况.此时如果不清楚 Maven 拉取依赖包的原理,那么很可能找不到问题所在.今天树哥就带大家了解下 Maven 拉包的原理,让你在遇到问题的时候能快速 ...
- [网络/Linux]网络嗅探工具——nmap
1 nmap 简介 Nmap 即 Network Mapper,最早是Linux下的网络扫描和嗅探工具包. nmap是网络扫描和主机检测的工具,用nmap进行信息收集和检测漏洞,功能有: 检测存活主机 ...
- 四月二十五号java基础知识
1.注意:无论哪个构造方法,在创建文件输入输出流时都可能银给出的文件名不对.路径不对文件的属性不对等,不能打开文件而造成错误,此时系统会抛出FileNotFoundException异常执行read( ...
- $\Beta$分布推导与可视化
$\Gamma$函数 $\Gamma$函数(Gamma函数)是阶乘函数在实数和复数域的扩展.对于正整数$n$,阶乘函数表示为$n! = 1 \times 2 \times ... \times n$. ...
- 链式描述线性表(C++实现)
在链式描述中,线性表的元素在内存中的存储位置是随机的,每个元素都有一个明确的指针或链指向下一个元素的位置 chain类 在此使用单向链表实现了线性表,其中最后一个节点的指针域为NULL,即它用单向链接 ...
- YOLO3论文中文版
文章目录 YOLO3论文中文版 摘要 1.引言 2. 解决方案 2.1 边界框预测 2.2 类预测 2.3 多尺度预测 2.4 特征提取器 2.5 训练 3.我们的做法 4. 失败的尝试 5.这一切意 ...
- 关于java中的多态和对实例化对象的一些理解
java面向对象三大特征即为:继承封装多态.而多态需要三大必要条件.分别是:继承.方法重写.父类引用指向子类对象.我们先一个一个来理解. 1.首先是继承和重写.这个很简单.因为多态就是建立在不同的重写 ...
- etcd:增加30%的写入性能
etcd:增加30%的写入性能 本文最终的解决方式很简单,就是将现有卷升级为支持更高IOPS的卷,但解决问题的过程值得推荐. 译自:etcd: getting 30% more write/s 我们的 ...
- 在Bamboo上怎么使用iOS的单元测试
作者:京东零售 吴滔 本教程将使用北汽登录模块为例,一步一步和大家一起搭建单元测试用例,并在Bamboo上跑起来,最终测试结果和代码覆盖率会Bamboo上汇总. 模块名称:BQLoginModule, ...
- 带你简单了解Chatgpt背后的秘密:大语言模型所需要条件(数据算法算力)以及其当前阶段的缺点局限性
带你简单了解Chatgpt背后的秘密:大语言模型所需要条件(数据算法算力)以及其当前阶段的缺点局限性 1.什么是语言模型? 大家或多或少都听过 ChatGPT 是一个 LLMs,那 LLMs 是什么? ...