A.Strong Password(贪心)
题意:
给定一个字符串 a a a,你需要在中间(可以是开头或结尾)插入一个字符,使得满足 a i ≠ a i + 1 a_i≠a_{i+1} ai=ai+1 的 i i i最多。
分析:
我们很容易贪心地想到在连续两个相同的字符中间插入一个字符贡献最大。否则在字符串结尾插入一个不同于结尾的字符。再特判一下一个字符和没有连续两个相同字符的情况即可。
代码:
#include <bits/stdc++.h>
using namespace std;
int main() {
int T;
cin >> T;
while (T--) {
string s;
cin >> s;
cout << s[0];
int flag = 0;
for (int i = 1; s[i]; i++) {
if (s[i] == s[i - 1] && !flag) {
flag = 1;
cout << char((s[i] - 'a' + 1) % 26 + 'a');
}
cout << s[i];
}
if (!flag) {
cout << char((s[s.size() - 1] - 'a' + 1) % 26 + 'a');
}
cout << endl;
}
return 0;
}
B.Make Three Regions (思维)
题意:
有一个 2 × n 2 \times n 2×n的网格,上面有些格子是障碍物,有些是空地。保证空地四连通。询问有多少个空地满足把它变成障碍物后,剩余的空地被分为三个四连通块。
分析:
只有这两种情况是符合题意的,我们进行特判即可。
x.x
...
...
x.x
代码:
#include <bits/stdc++.h>
using namespace std;
int n;
string s[2];
int main() {
int T;
cin >> T;
while (T--) {
cin >> n;
cin >> s[0];
cin >> s[1];
int ans = 0;
for (int i = 1; i < n - 1; i++)
if (s[0][i - 1] == 'x' && s[0][i + 1] == 'x' && s[0][i] == '.' && s[1][i + 1] == '.' && s[1][i - 1] == '.')
ans++;
for (int i = 1; i < n - 1; i++)
if (s[1][i - 1] == 'x' && s[1][i + 1] == 'x' && s[1][i] == '.' && s[0][i + 1] == '.' && s[0][i - 1] == '.')
ans++;
cout << ans << endl;
}
return 0;
}
C.Even Positions (贪心)
题意:
一个合法括号序列的权值定义为匹配括号的距离和。给出一个长度为 n n n的合法括号序列,但所有的奇数位置的括号都遗失了。你需要找出所有可能的原序列中,权值最小的为多少。
分析:
我们考虑到如果答案要求最小,那么就希望右括号能尽早配对。我们用栈维护前面所有的左括号,如果栈里还有左括号,那能配对就直接配对。剩下的部分一定是有解的。
在这样的操作下,每一步填完之后要么剩下两个左括号,要么什么也不剩下。因为最后一个字符一定是右括号,所以无论何时都是一定有解的。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int main() {
int T;
cin >> T;
while (T--) {
int n;
string s;
cin >> n >> s;
vector<int> tmp;
LL ans = 0;
for (int i = 0; i < n; i++) {
if (i % 2 == 0) {
if (tmp.empty()) {
tmp.push_back(i);
} else {
ans += i - tmp.back();
tmp.pop_back();
}
} else {
if (s[i] == '(')
tmp.push_back(i);
else {
ans += i - tmp.back();
tmp.pop_back();
}
}
}
cout << ans << endl;
}
return 0;
}
D. Maximize the Root (贪心)
题意:
给定一个有根树,由
n
n
n 个顶点组成。树中的顶点编号从
1
1
1 到
n
n
n ,根是顶点
1
1
1 。值
a
i
a_i
ai 写在第
i
i
i 个顶点处。
你可以执行以下任意次操作(可能是零次):选择一个顶点
v
v
v至少有一个子节点,将
a
v
a_v
av 增加
1
1
1 ,并将所有位于
v
v
v 子树中的顶点
u
u
u (除了
v
v
v 本身)的
a
u
a_u
au 减少
1
1
1 。但是,在每次操作之后,所有顶点上的值都应为非负值。
你的任务是使用上述操作计算在根上写入的最大可能值。
分析:
我们考虑贪心,先把所有子树能给出的颜色的最小值记录下来,记为 m a x v a l maxval maxval。如果当前根结点的值比 m a x v a l maxval maxval大,那么取 m a x v a l maxval maxval(因为根节点无法影响叶子结点),否则的话,根节点和 m x mx mx取平均,通过子树把当前根的贡献增大。
代码:
#include <bits/stdc++.h>
const int maxn = 3e5 + 10;
using namespace std;
int n, a[maxn], tmp[maxn];
vector<int> e[maxn];
void dfs(int u, int fa) {
int maxval = 0, flag = -1;
for (auto to: e[u])
if (to != fa) {
dfs(to, u);
if (flag == -1) {
flag = 1;
maxval = tmp[to];
} else {
maxval = min(tmp[to], maxval);
}
}
if (flag == -1) {
tmp[u] = a[u];
return;
}
if (u == 1) {
tmp[u] = a[1] + maxval;
return;
}
if (a[u] >= maxval)
tmp[u] = maxval;
else
tmp[u] = (a[u] + maxval) / 2;
}
int main() {
int T;
cin >> T;
while (T--) {
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i], e[i].clear();
for (int i = 2; i <= n; i++) {
int x;
cin >> x;
e[i].push_back(x);
e[x].push_back(i);
}
dfs(1, 0);
cout << tmp[1] << endl;
}
return 0;
}
E. Level Up (数据结构)
题意:
M o n o c a r p Monocarp Monocarp 正在玩电脑游戏。他以 1 1 1 的等级开始游戏。他即将与 n n n 个怪物战斗,顺序从 1 1 1 到 n n n 。第 i i i 个怪物的等级为 a i a_i ai 。
对于给定顺序中的每个怪物, M o n o c a r p Monocarp Monocarp 的遭遇如下:
- 如果 M o n o c a r p Monocarp Monocarp 的等级严格高于怪物的等级,则怪物逃跑;
- 否则, M o n o c a r p Monocarp Monocarp 与怪物战斗。
每与一个怪物进行 k k k 次战斗后(逃跑的怪物不计算在内), M o n o c a r p Monocarp Monocarp 的等级就会增加 1 1 1 。因此,在与 k k k 个怪物战斗后,他的等级变为 2 2 2 ,在与 2 k 2k 2k 个怪物战斗后,他的等级变为 3 3 3 ,在与 3 k 3k 3k 个怪物战斗后,他的等级变为 4 4 4 ,依此类推。
现在需要处理以下形式的 q q q 个查询:
- i x i~x i x :如果参数 k k k 等于 x x x , M o n o c a r p Monocarp Monocarp 会与第 i i i 个怪物战斗(或者这个怪物会逃跑)吗?
分析:
考虑到修改操作,所以我们想到预处理。对于每个
i
i
i,满足条件的
k
k
k是一个范围,即大于等于一个阈值
b
i
b_i
bi。那么我们需要做的,就是快速求出每个
i
i
i的阈值。
我们可以用二分来查找。再考虑开一颗权值线段树,记录当前的每个
k
k
k值对应的满足条件的下标数。每次计算出一个
b
i
b_i
bi,将线段树上
b
i
−
n
bi-n
bi−n的部分整体加
1
1
1。查询时单点查询即可。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define lson rt << 1
#define rson rt << 1 | 1
const LL N = 1e6 + 10;
LL n, q;
LL a[N], b[N], tr[N];
void build(LL rt, LL l, LL r) {
if (l == r) {
tr[rt] = 0;
return;
}
LL mid = (l + r) >> 1;
build(lson, l, mid);
build(rson, mid + 1, r);
}
void pushdown(LL rt, LL l, LL r) {
if (tr[rt]) {
tr[lson] += tr[rt];
tr[rson] += tr[rt];
tr[rt] = 0;
}
}
void change(LL rt, LL l, LL r, LL x, LL y, LL z) {
if (x <= l && r <= y) {
tr[rt] += z;
return;
}
pushdown(rt, l, r);
LL mid = (l + r) >> 1;
if (x <= mid)
change(lson, l, mid, x, y, z);
if (y > mid)
change(rson, mid + 1, r, x, y, z);
}
LL query(LL rt, LL l, LL r, LL x) {
if (l == r)
return tr[rt];
pushdown(rt, l, r);
LL mid = (l + r) >> 1;
if (x <= mid)
return query(lson, l, mid, x);
else
return query(rson, mid + 1, r, x);
}
int main() {
cin >> n >> q;
for (int i = 1; i <= n; i++) {
cin >> a[i];
b[i] = 0;
}
build(1, 1, n);
change(1, 1, n, 1, n, 1);
for (int i = 2; i <= n; i++) {
LL l = 1, r = n, res = 0;
while (l <= r) {
LL mid = (l + r) >> 1;
if (query(1, 1, n, mid) / mid + 1 > a[i])
l = mid + 1;
else
res = mid, r = mid - 1;
}
b[i] = res;
change(1, 1, n, res, n, 1);
}
while (q--) {
LL x, y;
cin >> x >> y;
if (b[x] <= y)
cout << "YES" << endl;
else
cout << "NO" << endl;
}
return 0;
}
赛后交流
在比赛结束后,会在交流群中给出比赛题解,同学们可以在赛后查看题解进行补题。
群号: 704572101,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。