Educational Codeforces Round 98 (Rated for Div. 2) (A - E题题解)
呼,熬过一场考试,补下题吧
A. Robot Program
在一个二维无限方格中,初始时你在格子里,每秒你有5种决策:选择移动到上下左右四个格子中的一个或者停留在原地。你不能连续两秒做相同的决策,问最短时间走到格子 \((x,y)\)。
「思路」
假设,首先花费步移动到,剩下就每次向上移动1秒再停留1秒即可。
时间复杂度:.
#include <bits/stdc++.h>
#define rep(i, s, t) for(register int i = (s), LIM=(t); i < LIM; ++i)
#define per(i, s, t) for(register int i = (s), LIM=(t); i >= LIM; --i)
using namespace std;
const int MXN = 2e5 + 5;
int n, m;
int ar[MXN];
void work() {
cin >> n >> m;
if(n > m) swap(n, m);
printf("%d\n", n * 2 + max(0, (m - n) * 2 - 1));
}
int main() {
ios::sync_with_stdio(false);cin.tie(0);
int tim;
cin >> tim;
for(int cas = 1; cas <= tim; ++ cas) work();
return 0;
}
B. Toy Blocks
有个黑盒,每个黑盒有个玩具,Jack只能选中一个黑盒,并用最佳策略将里面的玩具全部分出去放到其他盒子里面,只有当最有个盒子里的玩具个数相同的时候他才会开心。
你要确保无论Jack选中那一个黑盒,最后他都能高兴。为了达成这个目的,你可以给某些黑盒增加一些玩具,问最少增加多少个玩具可以达成这个目的。
「思路」
显然要让其他个盒子的玩具个数全相同的最佳方案就是能保证让盒子里的玩具个数都等于它们中个数的最大值。所以只需要考虑选中最大的盒子和次大的盒子两种方案即可。
若选中最大的盒子,最少需要增加个玩具;选中次打盒子,最少需要增加个玩具。两者取max就是答案,最后只需要把玩具给较小的几个盒子补充即可。
时间复杂度:.
#include <bits/stdc++.h>
#define rep(i, s, t) for(register int i = (s), LIM=(t); i < LIM; ++i)
#define per(i, s, t) for(register int i = (s), LIM=(t); i >= LIM; --i)
using namespace std;
const int MXN = 2e5 + 5;
int n, m;
int64_t ar[MXN], br[MXN];
void work() {
cin >> n;
int64_t ans = 0, sum = 0, mx = 0;
for(int i = 0; i < n; ++i) {
cin >> ar[i];
sum += ar[i];
mx = max(mx, ar[i]);
}
if(n == 2) ans = 0;
else ans = max((n - 1 - (sum % (n - 1)))%(n - 1), mx * (n - 1) - sum);
printf("%lld\n", ans);
}
int main() {
ios::sync_with_stdio(false);cin.tie(0);
int tim;
cin >> tim;
for(int cas = 1; cas <= tim; ++ cas) work();
return 0;
}
C. Two Brackets
给你一个字符串,每次操作是删掉字符串中的一个非空RBS子序列,问最多操作次数。
RBS串是:空串
或(RBS)
或[RBS]
或RBS+RBS
。
字符串总长度 \(<=2*10^5\)
「思路」
既然是最多操作次数,显然只用删()
和[]
即可。对每个右括号记录前面有多少个未匹配的左括号即可。
时间复杂度:\(O(n)\)
#include <bits/stdc++.h>
#define rep(i, s, t) for(register int i = (s), LIM=(t); i < LIM; ++i)
#define per(i, s, t) for(register int i = (s), LIM=(t); i >= LIM; --i)
using namespace std;
const int MXN = 2e5 + 5;
int n, m;
void work() {
string ar;
cin >> ar;
n = ar.length();
int ans = 0, a = 0, b = 0;
for(int i = 0; i < n; ++i) {
if(ar[i] == '(') ++ a;
else if(ar[i] =='[') ++ b;
else if(ar[i] == ')' && a) ++ ans, -- a;
else if(ar[i] == ']' && b) ++ ans, -- b;
}
printf("%d\n", ans);
}
int main() {
ios::sync_with_stdio(false);cin.tie(0);
int tim;
cin >> tim;
for(int cas = 1; cas <= tim; ++ cas) work();
return 0;
}
D. Radio Towers
给你一个代表有个城镇位于数轴到处,每个城镇有的概率建筑一个信号塔,你自己给第个信号塔分配信号强度为正整数,只要城镇满足即可搜到信号。同时你要保证两个条件:
- 城镇 \(0,n + 1\)不会搜到任何信号。
- 城镇 \([1,n]\)只能搜到一个信号塔的信号。
问能构建满足上述条件信号塔组合的概率,答案对998244353取模。
「思路」
赛后打前10项的表发现是斐波那契数列直接写,。orz
表示个城镇的合法信号塔组合的方案数,那么答案就是.
状态转移:\(dp[i] = dp[i - 1] + dp[i - 3] + dp[i-5] + ...+dp[i - 1 - 2 * k)]\)
其实就是枚举最后一步放一个带有信号塔的城镇且左右分别配上个城镇的转移。
因为只有左右城镇个数相同才是合法的转移。
初始化:\(dp[0] = dp[1] = dp[2] = 1\)
时间复杂度:.
#include <bits/stdc++.h>
#define rep(i, s, t) for(register int i = (s), LIM=(t); i < LIM; ++i)
#define per(i, s, t) for(register int i = (s), LIM=(t); i >= LIM; --i)
using namespace std;
const int mod = 998244353;
const int MXN = 2e5 + 5;
int n;
int64_t ar[MXN];
int64_t ksm(int64_t a, int64_t b, int kmod = mod) {
int64_t res = 1;
for(;b > 0; b >>= 1, a = (int64_t)a * a % kmod)
if(b & 1) res = (int64_t)res * a % kmod;
return res;
}
void work() {
cin >> n;
int64_t ans = 0, all = ksm(2, n);
ar[1] = 1, ar[2] = 1, ar[3] = 2;
for(int i = 4; i <= n; ++i) ar[i] = (ar[i - 1] + ar[i - 2]) % mod;
printf("%lld\n", ar[n] * ksm(all, mod - 2) % mod);
}
int main() {
ios::sync_with_stdio(false);cin.tie(0);
int tim = 1;
// cin >> tim;
for(int cas = 1; cas <= tim; ++ cas) work();
return 0;
}
E. Two Editorials
有个人参加一场有个题目比赛,有两个出题人再同一时间分别开讲题解大会,所以每个人只能去一个人讲题现场。每个出题人都只讲连续的题。对于第题到第题,第个人都想听,设第个人能听到个想听的题的题解。你来安排两个出题人的讲题区间,和参赛者去听谁的题解,使得最大,请输出这个最大值。
「思路」
假设第个参赛者的区间中点为 \(M\),左右两个讲题区间的中点为 \(A,B(A<=B)\)。
考虑一种贪心情况,对于第 \(i\) 个人必然是选择 \(A,B\)最靠近 \(M_i\)的,如果相同距离就任意选择一个即可。
所以我们先把参赛者按中点排序,一定是前一段区间参赛者 A听讲题,后一段区间参赛者 B听讲题。
再固定的情况下这样的贪心肯定没问题。接下来我们只需要枚举即可。
时间复杂度:.
#include <bits/stdc++.h>
#define rep(i, s, t) for(register int i = (s), LIM=(t); i < LIM; ++i)
#define per(i, s, t) for(register int i = (s), LIM=(t); i >= LIM; --i)
using namespace std;
const int MXN = 4e3 + 5;
int n, m, k;
class Node {
public:
int L, R;
bool operator<(const Node&b) const {
return L + R < b.L + b.R;
}
};
Node ar[MXN];
int64_t pre[MXN], cost[MXN][MXN];
int getcost(int a, int b, int c, int d) {
return max(0, min(b, d) - max(a, c) + 1);
}
void work() {
cin >> n >> m >> k;
rep(i, 0, m) {
cin >> ar[i].L >> ar[i].R;
++ pre[ar[i].L + ar[i].R];
}
rep(i, 1, n * 2 + 1) pre[i] = pre[i] + pre[i - 1];
sort(ar, ar + m);
for(int i = 1; i <= n; ++i) {
for(int j = 0; j <= m; ++j) {
cost[i][j] = getcost(i, min(i + k - 1, n), ar[j].L, ar[j].R);
if(j > 0) cost[i][j] += cost[i][j - 1];
}
}
int64_t ans = 0;
for(int fi = 1; fi <= n; ++fi) {
for(int se = fi; se <= n; ++se) {
int mid = (fi + fi + k - 1 + se + se + k - 1) / 2;
ans = max(ans, cost[fi][pre[mid] - 1] + cost[se][m - 1] - cost[se][pre[mid] - 1]);
}
}
printf("%lld\n", ans);
}
int main() {
ios::sync_with_stdio(false);cin.tie(0);
int tim = 1;
// cin >> tim;
for(int cas = 1; cas <= tim; ++ cas) work();
return 0;
}