A.Moving Chips (思维)
题意:
给一个长度为nnn的数组aaa,ai=1a_i=1ai=1或者ai=0a_i=0ai=0,现在可以选择一个 111,然后将其与左侧最近的000交换。询问使得所有的111连在一起,中间没有000至少需要多少次操作。
分析:
可以发现答案为从第一个111到最后一个111的子串中000的数量。
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
const int INF = 0x3f3f3f3f;
int a[N];
int main() {
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
int l = INF, r = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i];
if (a[i] == 1)
l = min(i, l), r = max(i, r);
}
int ans = 0;
for (int i = l; i <= r; i++) {
if (a[i] == 0)
ans++;
}
cout << ans << endl;
}
return 0;
}
B.Monsters Attack! (前缀和)
题意:
有nnn个怪物在一个数轴上,每个怪物位于xix_ixi处,有生命值hih_ihi,玩家处于数轴000位置处,每一秒都会进行以下操作:
- 玩家选择一些怪物,并为他们分配总计kkk点伤害。
- 怪物生命值小于等于000认定死亡
- 所有生命值大于000的怪物都向玩家所在位置前进一个单位。如果存在怪物到达000,玩家失败。
分析:
对于第iii个怪物,需要在xix_ixi秒前造成至少kkk点伤害,num[i]num[i]num[i]表示1−i1-i1−i秒需要造成的伤害,如果伤害大于kkk,就表明失败。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int main() {
int t;
cin >> t;
while (t--) {
LL n, k;
cin >> n >> k;
LL a[n + 1], x[n + 1];
vector<LL> num(n + 1, 0);
for (int i = 1; i <= n; i++)
cin >> a[i];
for (int i = 1; i <= n; i++)
cin >> x[i];
for (int i = 1; i <= n; i++)
num[abs(x[i])] += a[i];
for (int i = 1; i <= n; i++)
num[i] += num[i - 1];
int flag = 0;
for (int i = 1; i <= n; i++) {
if (num[i] > i * k) {
flag = 1;
break;
}
}
if (flag)
cout << "NO" << endl;
else
cout << "YES" << endl;
}
return 0;
}
C. Find B (思维)
题意:
一个长度为mmm的数组aaa被认为是好的,当且仅当满足下列条件:
- 存在数组bbb,bi>0,ai≠bib_i>0,a_i \neq b_ibi>0,ai=bi,∑i=1mai=∑i=1mbi\sum\limits_{i=1}^{m} a_i=\sum\limits_{i=1}^{m} b_ii=1∑mai=i=1∑mbi
给出一个数组ccc和区间l,rl,rl,r,询问子数组cl∼crc_l \sim c_rcl∼cr是不是好的。
分析:
长度为111的区间显然无解。对于长度不为111的区间,如果所有的元素都大于111,那么可以将所有数大于111的部分加到其中一个数字上,一定有解。如果区间内存在若干个111,那么需要判断区间内大于111的部分能否将区间内的每个111都至少加上111。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int main() {
int t;
cin >> t;
while (t--) {
int n, q;
cin >> n >> q;
vector<LL> sum(n + 1), cnt1(n + 1);
for (int i = 1; i <= n; i++) {
int x;
cin >> x;
sum[i] = sum[i - 1] + x;
cnt1[i] = cnt1[i - 1] + (x == 1);
}
while (q--) {
int l, r;
cin >> l >> r;
if (l == r) {
cout << "NO" << endl;
continue;
}
LL tmp = sum[r] - sum[l - 1] - (r - l + 1);
if (tmp >= cnt1[r] - cnt1[l - 1]) {
cout << "YES" << endl;
} else
cout << "NO" << endl;
}
}
return 0;
}
D.Slimes (二分)
题意:
有nnn个史莱姆排成一排,第iii个史莱姆的大小为aia_iai,每一秒都会进行如下操作:
- 选择两个大小不同的并且相邻的史莱姆,较大的史莱姆将吞噬较小的史莱姆,且大小变为两者之和。
询问对于每一个史莱姆,最少需要几秒才能被吞噬,如果不可能被吞噬,输出−1-1−1。
分析:
对于一个史莱姆,可以从左右两个方向来选择一段区间合并出一个大史莱姆将其吞噬,首先需要保证区间内数字和大于aia_iai,可以通过二分出满足条件的左右端点。其次需要判断区间内的数字是否不完全相同,只要不完全相同都可以通过吞噬得到一个更大的史莱姆,可以通过维护一个数字往前/往后第一个与其不同的数字的位置来进行判断。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int INF = 0x3f3f3f3f;
int main() {
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
vector<int> a(n + 5);
vector<LL> sum(n + 5);
for (int i = 1; i <= n; i++) {
cin >> a[i];
sum[i] = sum[i - 1] + a[i];
}
vector<int> pre(n + 5), suf(n + 5);
for (int i = 2; i <= n; i++) {
if (a[i] == a[i - 1]) {
pre[i] = pre[i - 1];
} else {
pre[i] = i - 1;
}
}
suf[n] = n + 1;
for (int i = n - 1; i >= 1; i--) {
if (a[i] == a[i + 1]) {
suf[i] = suf[i + 1];
} else {
suf[i] = i + 1;
}
}
for (int i = 1; i <= n; i++) {
int ans = INF;
if (sum[i - 1] > a[i]) {
int l = 0, r = i - 1;
while (l < r) {
int mid = (l + r + 1) / 2;
if (sum[i - 1] - sum[mid - 1] > a[i])
l = mid;
else
r = mid - 1;
}
if (r == i - 1) {
ans = min(ans, 1);
} else if (pre[i - 1] != 0) {
ans = min(ans, max(i - r, i - pre[i - 1]));
}
}
if (sum[n] - sum[i] > a[i]) {
int l = i + 1, r = n;
while (l < r) {
int mid = (l + r) / 2;
if (sum[mid] - sum[i] > a[i])
r = mid;
else
l = mid + 1;
}
if (r == i + 1) {
ans = min(ans, 1);
} else if (suf[i + 1] != n + 1) {
ans = min(ans, max(r - i, suf[i + 1] - i));
}
}
if (ans == INF)
ans = -1;
cout << ans << " ";
}
cout << endl;
}
return 0;
}
E.Count Paths (启发式合并)
题意:
给出一棵nnn个节点的树,每个节点有着颜色cic_ici,询问满足以下条件的路径的数量:
- 至少经过两个节点
- 起始节点和终止节点颜色相同,并且和路径上的节点的颜色不相同
分析:
有两种满足要求的路径:
- 一个点是另一个点的祖先
- 两个节点没有祖先关系
遍历每一个节点,使用mapmapmap记录子树内没有被祖先覆盖过的每种颜色的点的数量,再利用启发式合并来进行统计,满足上述要求的两种路径都可以在启发式合并的过程中求出来。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int main() {
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
vector<LL> c(n + 5);
vector<vector<LL>> g(n + 5);
for (int i = 0; i < n; i++)
cin >> c[i];
for (int i = 1; i < n; i++) {
int u, v;
cin >> u >> v;
u--, v--;
g[u].push_back(v);
g[v].push_back(u);
}
vector<map<LL, LL>> dp(n + 5);
LL ans = 0;
auto dfs = [&](auto self, int u, int p) -> void {
for (auto v: g[u]) {
if (v == p)
continue;
self(self, v, u);
}
for (auto v: g[u]) {
if (v == p)
continue;
if (dp[v].size() > dp[u].size()) {
swap(dp[u], dp[v]);
}
}
for (auto v: g[u]) {
if (v == p)
continue;
for (auto [x, y]: dp[v]) {
if (x != c[u])
ans += y * dp[u][x];
dp[u][x] += y;
}
}
ans += dp[u][c[u]];
dp[u][c[u]] = 1;
};
dfs(dfs, 0, -1);
cout << ans << endl;
}
return 0;
}
赛后交流
在比赛结束后,会在交流群中给出比赛题解,同学们可以在赛后查看题解进行补题。
群号: 704572101,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。

1787

被折叠的 条评论
为什么被折叠?



