Educational Codeforces Round 136 (Rated for Div. 2) A-E

比赛链接

A

题解

知识点:模拟。

所有点都跑一遍即可。

另外可以不模拟, 22 的情况都可以摆在 (2,2) 这个点,其他摆在 (1,1)

时间复杂度 O(nm)

空间复杂度 O(1)

代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int dir[8][2] = { {-2,1},{-1,2},{1,2},{2,1},{2,-1},{1,-2},{-1,-2},{-2,-1} };
bool solve() {
int n, m;
cin >> n >> m;
int x = n, y = m;
for (int i = 1;i <= n;i++) {
for (int j = 1;j <= m;j++) {
bool ok = false;
for (int k = 0;k < 8;k++) {
int xx = i + dir[k][0];
int yy = j + dir[k][1];
ok |= xx >= 1 && xx <= n && yy >= 1 && yy <= m;
}
if (!ok) {
x = i;
y = j;
break;
}
}
}
cout << x << ' ' << y << '\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

题解

知识点:贪心。

显然,根据 di 可以得到 ai=ai1±di ,但 ai0 ,因此如果 di0ai1di0 时说明有两个合法 ai 输出 1 ,否则继续构造。

时间复杂度 O(n)

空间复杂度 O(n)

代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;
int d[107];
bool solve() {
int n;
cin >> n;
for (int i = 1;i <= n;i++) cin >> d[i];
for (int i = 2;i <= n;i++) {
if (d[i] && d[i - 1] - d[i] >= 0) return false;
d[i] += d[i - 1];
}
for (int i = 1;i <= n;i++) cout << d[i] << ' ';
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

题解

知识点:排列组合,构造,线性dp。

总局面一共 Cnn2 种。

注意到平局只有 1 种情况,即每次最大的牌都在对方手里,而自己手里有次大的牌,如此就能构造出平局的局面了。比如在 n=8 时,平局为 A 拥有 7632B8541 。因此只要求出 A 赢局面,就能得到 B 赢局面。

如果 A 的局面比其平局局面的从大到小的某一回合牌更大(前面回合的牌和平局一样,后面的牌随意分配),那么 A 赢。如 A764176 一样但 43 大,因此 A 赢。对 B 也是同理的。

再者,我们注意到,局面情况是可以递推的。如 n=8 时第一回合结束就来到 n=6 的情况,不过 B 成先手了。

因此我们对 n=i 的局面,可以先求出第一回合的情况,再用 n=i2 的结果即可。

显然第一回合 A 赢的情况,只有牌是最大 i 的时候,此时剩下的牌可以随便选,一共有 Ci1i21 种,接下来的情况和 n=i2B 赢等价。

我们定义 ansA[i]n=iA 赢的局面数,ansB[i]n=iB 赢的局面数。 有递推关系:

ansA[i]=Ci1i21+ansB[i2]ansB[i]=Cii2ansA[i]1

时间复杂度 O(602)

空间复杂度 O(602)

代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int mod = 998244353;
int ansA[67], ansB[67];
int C[67][67];
void init(int n = 60) {
for (int i = 0;i <= n;i++) {
for (int j = 0;j <= i;j++) {
if (j == 0 || i == j) C[i][j] = 1;
else C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
}
}
}
void get(int n = 60) {
ansA[2] = 1;
ansB[2] = 0;
for (int i = 4;i <= n;i += 2) {
ansA[i] = (C[i - 1][i / 2 - 1] + ansB[i - 2]) % mod;
ansB[i] = (C[i][i / 2] - ansA[i] - 1 + mod) % mod;
}
}
bool solve() {
int n;
cin >> n;
cout << ansA[n] << ' ' << ansB[n] << ' ' << 1 << '\n';
return true;
}
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
init();
get();
while (t--) {
if (!solve()) cout << -1 << '\n';
}
return 0;
}

D

题解

知识点:二分,树形dp。

这道题答案具有单调性,因此可以二分答案 mid

dp[u] 为节点 u 希望的高度。自底向上处理,把叶子节点希望高度设为 mid 。父节点的希望高度为所有子节点希望高度最小值减 1

dp[u]=1 时,除了节点 1 和父节点是 1 的节点,都属于需要操作的节点,因此操作次数加 1 。但要注意,操作过的节点希望高度需要设为 mid+1 ,这个子节点已经与原来的父节点分离了,其希望高度不能影响到原来的父节点,所以设为父节点可能的希望高度最大值加 1

最后判断一下操作次数有没有超过 k 来进行二分即可。

时间复杂度 O(nlogn)

空间复杂度 O(n)

代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;
vector<int> g[200007];
int dp[200007];
int n, k;
int mid, cnt;
void dfs(int u, int fa) {
dp[u] = mid;
for (auto v : g[u]) {
if (v == fa) continue;
dfs(v, u);
dp[u] = min(dp[u], dp[v] - 1);
}
if (dp[u] == 1 && fa != 1 && u != 1) {
cnt++;
dp[u] = mid + 1;
}
}
bool check(int mid) {
cnt = 0;
dfs(1, 0);
return cnt <= k;
}
bool solve() {
cin >> n >> k;
for (int i = 1;i <= n;i++) g[i].clear();
for (int i = 2;i <= n;i++) {
int u;
cin >> u;
g[i].push_back(u);
g[u].push_back(i);
}
int l = 1, r = n - 1;
while (l <= r) {
mid = l + r >> 1;
if (check(mid)) r = mid - 1;
else l = mid + 1;
}
cout << l << '\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

题解

知识点:状压dp。

dp[i][j] 表示清理到第 i 列第 j 行格子且第 i1 列及以前的格子也都清理时,机器人清理的最大格子数。

假设机器人走到了第 i 列第 j 行(接下来称为 (i,j) ),接下来有两种走法:继续往右直走,换另一行往右直走。

  1. 第一种情况往右直走,此时我们需要手动保证 (i,1j) 没垃圾,但其影响不了机器人下一步行动,所以可以直接转移:

    dp[i + 1][j] = max(dp[i + 1][j], dp[i][j] + (s[j][i + 1] == '1'));
  2. 第二种情况换行往右直走,但这种情况建立在 (i,1j) 有垃圾,否则机器人不会换行。此时我们需要手动保证 (i+1,j) 没垃圾,但不能直接转移到 (i+1,1j) ,因为此时转移的状态是建立在 (i+1,j) 一定没有垃圾的情况。但 (i+1,1j) 也有可能会从 (i+1,j) 转移过来,这个转移是没有限制的。因此转移的前提都不同了,所以第二种转移具有后效性。解决方案是继续顺着走到 (i+2,1j),既然 (i+1,j) 在这种情况下保证没有垃圾了,不妨继续走到 (i+2,1j) ,此时是没有后效性的,转移方程为:

    dp[min(i + 2, n)][j ^ 1] = max(dp[min(i + 2, n)][j ^ 1], dp[i][j] + 1 + (s[j ^ 1][i + 1] == '1') + (s[j ^ 1][i + 2] == '1'));

时间复杂度 O(n)

空间复杂度 O(n)

代码

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