Codeforces Round #821 (Div. 2) A-E

比赛链接

A

题解

知识点:贪心。

下标模 k 相同分为一组,共有 k 组,组间不能互换,组内任意互换。

题目要求连续 k 个数字,一定能包括所有的 k 组,现在只要在每组中选取最大的加在一起即可。

时间复杂度 O(n)

空间复杂度 O(n)

代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;
int a[107];
bool solve() {
int n, k;
cin >> n >> k;
for (int i = 1;i <= n;i++) cin >> a[i];
ll ans = 0;
for (int i = 1;i <= k;i++) {
int mx = 0;
for (int j = i;j <= n;j += k)
mx = max(mx, a[j]);
ans += mx;
}
cout << ans << '\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

题解

知识点:构造。

注意到,必然会有人赢 0 次,有人会赢非 0 次,因此 x,y=0 或者 x,y0 都是不存在的。

假设非 0 数为 x ,即赢的人都赢 x 次,其他人都直接输掉,因为共有 n1 次机会,那么只有 x|n1 时合法,其余情况不存在。

在合法的情况下,尝试构造。我们只需要确定第一场赢的人,然后每隔 x 场换个人即可。为了方便,我们确定 2 为第一场赢的人,这样我们对人的编号加 x 就是下一个人。

时间复杂度 O(n)

空间复杂度 O(n)

代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;
bool solve() {
int n, x, y;
cin >> n >> x >> y;
if (x && y || !x && !y) return false;
if (x < y) swap(x, y);
if ((n - 1) % x) return false;
else {
for (int i = 2;i <= n;i += x) {
for (int j = 1;j <= x;j++)
cout << 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

题解

知识点:构造。

考虑通过 n1 次操作使得序列元素全部相等。

注意到同奇同偶和为偶数使数字向左转移,否则为奇数使数字向右转移。

显然,可以通过第一个数字把右侧所有与之奇偶性不同的数字变成相等的,现在考虑与第一个数字奇偶性相同的数字相等。

因为同奇偶性,所以只能向左转移。考虑找到最后一个与第一个数字同奇偶性的数字,随后通过这个数字向左把同奇偶性的数字都变为相等,之后再执行上一步把不同奇偶性的数字变成相等的。

上述操作,每次都能增加一个与初始数字确定相等的数字,所以共操作 n1 次。

时间复杂度 O(n)

空间复杂度 O(n)

代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;
bool vis[100007];
bool solve() {
int n;
cin >> n;
int ls = 1;
for (int i = 1, x;i <= n;i++) {
cin >> x;
vis[i] = x & 1;
if (vis[1] == vis[i]) ls = i;
}
cout << n - 1 << '\n';
for (int i = ls - 1;i >= 1;i--) {
if (vis[i] == vis[1])
cout << i << ' ' << ls << '\n';
}
for (int i = 2;i <= n;i++) {
if (vis[1] != vis[i])
cout << 1 << ' ' << i << '\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

题解

知识点:区间dp,贪心。

任何时候,如果需要修改的地方是奇数,则不存在解法,即 1 情况。

简单版本,规定 xy ,因此:

  1. 当需要修改的位置 cnt4 时,可以每次隔一个取,使花费永远是 y ,最终花费是 cnt2y
  2. cnt=2 时,除了花费 x 直接消掉,还可以通过一个其他不相邻的位置中转,两次不相邻修改实现一次相邻修改,花费 2y ,因此总花费是 min(x,2y)

困难版本,没有规定 x,y 关系,但 xy 那部分可以直接用贪心结论解决,x<y 只能dp解决了,这里采用线性dp,其他dp也能做。

dp[i] 表示从 1i 修改成一样的最小花费。

i 为偶数时,dp[i]=min(dp[i2]+(v[i]v[i1])x,dp[i1]+y)

  1. 修改了前 i2 个位置的花费,加上链式 x 修改第 i1i 个的花费。
  2. 修改了前 i1 个位置除了一个位置没修改(可能在任何位置,也即包括了在第 i1 处)的花费,加上不相邻修改 y 的花费。

取最小值。其中前者修改不需要考虑 y ,因为后者包括了;后者不需要考虑 x 链式修改,可以证明不可能更优。

i 为奇数时,dp[i]=min(dp[i2]+(v[i]v[i1])x,dp[i1])

  1. 修改了前 i2 个位置除了一个位置没修改的花费,加上链式 x 修改第 i1i 个的花费。
  2. 修改了前 i1 个位置的花费,留下第 i 处没修改。

取最小值。其中前者修改不需要考虑 y 不相邻修改,因为前者如果使用 y 修改 i1i,花费等价于 dp[i1] 的情况之一,即 dp[i2]+ydp[i1] ,后者更优在于保留了第 i 个位置而非更前的位置,对偶数情况有更好的影响。

时间复杂度 O(n)

空间复杂度 O(n)

代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll dp[5007];
bool solve() {
int n;
ll x, y;
string a, b;
vector<int> v;
cin >> n >> x >> y;
cin >> a >> b;
v.clear();
for (int i = 0;i < n;i++) {
if (a[i] != b[i]) v.push_back(i);
}
if (v.size() & 1) return false;
if (!v.size()) {
cout << 0 << '\n';
return true;
}
if (x >= y) {
if (v.size() == 2 && v.front() + 1 == v.back()) cout << min(x, 2 * y) << '\n';
else cout << v.size() / 2LL * y << '\n';
}
else {
dp[0] = 0;
dp[1] = min((v[1] - v[0]) * x, y);
for (int i = 2;i < v.size();i++) {
if (i & 1) dp[i] = min(dp[i - 2] + (v[i] - v[i - 1]) * x, dp[i - 1] + y);
else dp[i] = min(dp[i - 2] + (v[i] - v[i - 1]) * x, dp[i - 1]);
}
cout << dp[v.size() - 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;
}

E

题解

知识点:数学,线性dp。

显然,直接求第 t(x,y) 是否有球非常困难,但是求 [0,t] 秒内经过 (x,y) 的数量(包括这个格子本身)是比较容易的。

每个位置都是方向右下交替进行,且初始是朝右。因此,假设 [0,t] 秒内经过 (i,j) 的球有 n 个,那么可以得知 [0,t+1] 秒内经过 (i+1,j) 的球有 n2 个,经过 (i,j+1) 的球有 n2 个。

但是,在 t+1 秒及其之后到达 (i,j) 的球不可能在 [0,t+1] 秒内经过 (i+1,j)(i,j+1) ,因为从 (i,j)(i+1,j)(i,j+1) 需要 1 秒。因此,要求 [0,t] 秒内经过 (x,y) 的数量,则只需要 [0,t(i+j)] 秒内经过 (i,j) 的数量。

我们知道 [0,t] 内经过 (0,0) 的球有 t+1 个。因此,能递推得出 [0,t] 秒内经过 (x,y) 的球的个数。。

dp[i][j][0,t] 秒内 (i,j) 经过了多少能到达 (x,y) 的球的数量,初始条件是 dp[0][0]=min(t(x+y)+1,0) 。转移方程为:

dp[i + 1][j] += dp[i][j] / 2;
dp[i][j + 1] += dp[i][j] - dp[i + 1][j];

时间复杂度 O(q)

空间复杂度 O(1)

代码

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