比赛链接

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实现(二)

    M/M/1随机服务系统的模拟 M/M/1模型是一种出生-死亡过程,此随机过程中的每一个状态代表模型中人数的数目.因为模型的队列长度无限且参与人数亦无限,故此状态数目亦为无限.例如状态0表示模型闲置.状 ...

  2. 【Vue项目】尚品汇(五)Detail组件开发 实现轮播图和放大镜效果

    1 基本准备工作 1.1 组件路由及数据准备 编写请求接口 api/index.js export const reqGetDetailInfo = (skuId ={}) => { retur ...

  3. Problems with EXC_BAD_ACCESS in CCBReader

    Hi guys, I've found problems using the CCBReader when deploying my game to an iPhone 4.There are sev ...

  4. 【介绍】C++五种迭代器

    目录 1. 输入迭代器(Input Iterator): 2. 输出迭代器(Output Iterator): 3. 前向迭代器(Forward Iterator): 4. 双向迭代器(Bidirec ...

  5. vue3.0

    https://www.yuque.com/gdnnth/vue-v3 http://www.liulongbin.top:8085/#/ https://www.yuque.com/woniuppp ...

  6. 新概念英语(New Concept English),前言

    本书向读者提供了一套完整的,经过实践检验的英语学习体系,使得学生能够发挥自己的最大潜能. 听力 口语 阅读 写作 学习语言不在于掌握一套规则和积累大量词汇. 而在于如何运用所学的知识. 学习单词,必须 ...

  7. .gitignore 文件语法介绍

    .gitignore 文件的作用 A gitignore file specifies intentionally untracked files that Git should ignore. Fi ...

  8. #Python 利用pandas 合并csv/xlsx文件

    上次我们分享了利用powerquery来合并文件进行数据分析,但是Pq有一部分局限性,在现实工作中,我们往往需要合并多个文件去处理数据, 如果面对20个甚至更多的文件,pq中的每一步的步骤都会去读取每 ...

  9. stl------iterator迭代器与Vector

    Vector不定长数组 例:http://newoj.acmclub.cn/contests/1258/problem/4 1926: 2018蓝桥杯培训-STL应用专题-day 2 vector作业 ...

  10. react之todoList基础小项目

    1.项目最终成品和项目目录快照如图: 2.context.js文件 // 使用context进行多级传递数据 // 1. createContext 创建一个可以多级传递的context数据 // 2 ...