Educational Codeforces Round 137 (Rated for Div. 2) A-F

比赛链接

A

题解

知识点:数学。

4 位密码,由两个不同的数码组成,一共有 C42 种方案。从 10n 个数字选两个,有 C10n2 种方案。结果为 3(10n)(9n)

时间复杂度 O(n)

空间复杂度 O(1)

代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;
bool solve() {
int n;
cin >> n;
for (int i = 1;i <= n;i++) {
int x;
cin >> x;
}
cout << 3 * (10 - n) * (9 - n) << '\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

题解

知识点:构造。

显然最小价值是 2 。把 12 分两端放即可。

时间复杂度 O(n)

空间复杂度 O(1)

代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;
bool solve() {
int n;
cin >> n;
for (int i = 2;i <= n;i++) cout << i << ' ';
cout << 1 << '\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

题解

知识点:贪心。

发现,可以将一段连续的盖子向左移,等效于把最后一个盖子移到第一个左边。因此,找到一个无盖且右边连续有盖的位置,从连续有盖的一段选一个最小的和无盖的位置交换,如果最小的比无盖的大那就不交换。

时间复杂度 O(n)

空间复杂度 O(n)

代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;
int a[200007];
bool solve() {
int n;
cin >> n;
string s;
cin >> s;
s = "?" + s;
for (int i = 1;i <= n;i++) cin >> a[i];
for (int i = 1;i <= n - 1;i++) {
if (s[i] == '0' && s[i + 1] == '1') {
int mi = i;
for (int j = i + 1;j <= n && s[j] == '1';j++) {
if (a[j] < a[mi]) mi = j;
}
swap(s[i], s[mi]);
}
}
int sum = 0;
for (int i = 1;i <= n;i++) {
if (s[i] == '1') sum += a[i];
}
cout << sum << '\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

题解

知识点:枚举。

第一个串显然是从第一个 1 开始截取到最后,现在考虑从第一个串截取出第二个串填补第一个串的 0

显然要从第一个 0 开始填。注意到,只有 0 前面的 1 可以通过截取向右移动与 0 重合。枚举前面每个 1 通过截取与第一个 0 重合即可,截取方法有很多,第一种,以各个 1 为起点,截取固定长度,保持 10 重合;第二种,从第一个 1 开始截取,截取不同长度,使各个 10 重合(这种适合用 bitset)。

发现复杂度是和从第一个 1 开始连续 1 的长度有关,但因为是随机数据,因此不可能太长,可以看作常数。

时间复杂度 O(n)

空间复杂度 O(n)

代码

第一种

#include <bits/stdc++.h>
using namespace std;
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n;
cin >> n;
string s;
cin >> s;
int pos = s.find('1');
if (!~pos) {
cout << 0 << '\n';
return 0;
}
string s1 = s.substr(pos);
pos = s1.find('0');
string ans = s1;
for (int i = 0;i < pos;i++) {
string s2 = s1;
for (int j = pos;j < s1.size();j++) {
if (s1[j - pos + i] == '1') s2[j] = '1';
}
ans = max(ans, s2);
}
cout << ans << '\n';
return 0;
}

第二种

#include <bits/stdc++.h>
using namespace std;
string elz(string s) {
for (int i = 0;i < s.size() - 1;i++)
if (s[i] == '1') return s.substr(i);
return s.substr(s.size() - 1);
}
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n;
string s;
cin >> n >> s;
bitset<1000005> a(s), b(s);
string ans = a.to_string();
for (int i = 1;i <= 10;i++)
ans = max(ans, (a | (b >> i)).to_string());
cout << elz(ans) << '\n';
return 0;
}

E

题解

知识点:线性dp。

考虑设 dp[i] 为造成不少于 i 点伤害的最小时间。

先考虑 p1,p2 单独射击的转移:

dp[i] = min(dp[max(0LL, i - (p1 - s))] + t1, dp[max(0LL, i - (p2 - s))] + t2);

再考虑,p2p1j 轮时间中调整,配合 p1 在第 j 轮同时射击。此时 p1 伤害是 (j1)(p1s)p2 伤害是 jt1t2t2(p2s) ,同时射击伤害 p1+p2s ,共计 (j1)(p1s)+jt1t2t2(p2s)+(p1+p2s) 。(同理 p1 配合 p2 同时射击)。

ll dmg = (j - 1) * (p1 - s) + (t1 * j - t2) / t2 * (p2 - s) + (p1 + p2 - s);
dp[i] = min(dp[i], dp[max(0LL, i - dmg)] + t1 * j);

时间复杂度 O(h2)

空间复杂度 O(h)

代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll dp[5007];
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int p1, p2;
ll t1, t2;
cin >> p1 >> t1;
cin >> p2 >> t2;
int h;
ll s;
cin >> h >> s;
dp[0] = 0;
for (int i = 1;i <= h;i++) {
dp[i] = min(dp[max(0LL, i - (p1 - s))] + t1, dp[max(0LL, i - (p2 - s))] + t2);
for (int j = 1;j <= i;j++) {
if (t1 * j >= t2) {
ll dmg = (j - 1) * (p1 - s) + (t1 * j - t2) / t2 * (p2 - s) + (p1 + p2 - s);
dp[i] = min(dp[i], dp[max(0LL, i - dmg)] + t1 * j);
}
if (t2 * j >= t1) {
ll dmg = (j - 1) * (p2 - s) + (t2 * j - t1) / t1 * (p1 - s) + (p1 + p2 - s);
dp[i] = min(dp[i], dp[max(0LL, i - dmg)] + t2 * j);
}
}
}
cout << dp[h] << '\n';
return 0;
}

F

题解

知识点:排列组合,枚举。

我们考虑每个点对答案的贡献,显然是从他第一次出现开始计算。

比如某点在第 i 组区间出现,那么已经进行了 i2 次操作了,产生 3i2 个集合。在这之后还有 ni+1 次操作,如果操作时另一个区间有这个点,那么或和且能继承这个点继续;如果没有这个点,那么或和异或能继承这个点继续,注意无论以后有没有这个点都只有两个分支能使这个点有贡献,所以共 2ni+1 个集合是有这个点的,最后总贡献是 3i22ni+1

要特别注意,第一个区间出现的点,之前的操作也是 0 次 ,并且之后的操作也只有 n1 次,最终通式是 3max(i2,0)2min(ni+1,n1)

接下来考虑如何记录某个点第一次出现的位置 lst[i] 。朴素枚举是 n2 的,我们需要优化。显然出现过的点之后跳过就行了,我们记录每个点能跳跃到的位置 nxt[i] 即可,对于区间 [l,r] ,如果某个点 i 没出现过,那么 nxt[i] 可以更新为 r[i] ;否则出现过,我们需要跳跃,同时更新这个点以后能跳跃到的位置,nxt[i]=max(nxt[i],r),i=nxt[i] ,注意需要保存一下旧位置,再更新完 nxt[i] 再更新 i

时间复杂度 O(n)

空间复杂度 O(n)

代码

#include <bits/stdc++.h>
using namespace std;
const int mod = 998244353;
int l[300007], r[300007], lst[300007], nxt[300007];
int pow2[300007], pow3[300007];
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n;
cin >> n;
memset(lst, -1, sizeof(lst));
pow2[0] = 1;
pow3[0] = 1;
for (int i = 1;i <= n;i++) {
pow2[i] = 1LL * pow2[i - 1] * 2 % mod;
pow3[i] = 1LL * pow3[i - 1] * 3 % mod;
}
for (int i = 1;i <= n;i++) cin >> l[i] >> r[i];
for (int i = n;i >= 1;i--) {
for (int j = l[i];j <= r[i];j++) {
if (lst[j] == -1)lst[j] = i, nxt[j] = r[i];
else {
int tmp = nxt[j];
nxt[j] = max(nxt[j], r[i]);
j = tmp;
}
}
}
int ans = 0;
for (int i = 0;i <= 300000;i++) {
if (lst[i] == -1) continue;
ans = (ans + 1LL * pow3[max(lst[i] - 2, 0)] * pow2[min(n - lst[i] + 1, n - 1)]) % mod;
}
cout << ans << '\n';
return 0;
}
posted @   空白菌  阅读(219)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
点击右上角即可分享
微信分享提示