Codeforces Round #792 (Div. 1 + Div. 2) A-E

比赛链接

A

题解

知识点:数学。

显然长度大于等于3的数字串的最小数位是完全可以通过这些操作留到最后。

长度等于2的数字串只可能是个位数字。

时间复杂度 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 t;
cin >> t;
while (t--) {
string str;
cin >> str;
if (str.length() == 2) cout << str.back() << '\n';
else {
int ans = ~(1 << 31);
for (int i = 0;i < str.length();i++) ans = min(ans, str[i] - '0');
cout << ans << '\n';
}
}
return 0;
}

B

题解

知识点:数学,构造。

容易得到三个式子

x=ky+ay=mz+bz=nx+c

其中 k,m,nN+

通过代入得到

x=kmc+kb+a1kmn

由于 zmodx=c ,因此有 x>c ,考虑 k=1,m=1,n=0 得到 x=a+b+c,y=b+c,z=c

时间复杂度 O(1)

空间复杂度 O(1)

代码

#include <bits/stdc++.h>
using namespace std;
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t;
cin >> t;
while (t--) {
long long a, b, c;
cin >> a >> b >> c;
cout << a + b + c << ' ' << b + c << ' ' << c << '\n';
}
return 0;
}

C

题解

知识点:模拟,排序。

通过排序完数组和原来的对比数组,找到第一组两个在同一行需要交换位置的点,如果超过2个同一行的需要交换的点说明一次交换不可行直接输出-1,如果为0个则不需要交换,设置任意一个点作为两个交换的点(即不交换)。

在确认两个交换点之后,对原数组每行进行交换,每行交换之后判断是否是排序好的,如果有一行不是说明不可行输出-1,否则为答案。

时间复杂度 O(nmlogm)

空间复杂度 O(nm)

代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;
bool solve() {
int n, m;
cin >> n >> m;
vector<vector<int>> a(n, vector<int>(m)), b(n, vector<int>(m));
for (int i = 0;i < n;i++) {
for (int j = 0;j < m;j++) {
cin >> a[i][j], b[i][j] = a[i][j];
}
}
for (int i = 0;i < n;i++) {
sort(b[i].begin(), b[i].end());
}
int pos1 = -1, pos2 = -1;
for (int i = 0;i < n;i++) {
for (int j = 0;j < m;j++) {
if (a[i][j] != b[i][j]) {
if (!~pos1) pos1 = j;
else if (!~pos2) pos2 = j;
else return false;
}
}
if (~pos1) break;
}
if (!~pos1) pos2 = pos1 = 0;
for (int i = 0;i < n;i++) {
swap(a[i][pos1], a[i][pos2]);
if (!is_sorted(a[i].begin(), a[i].end())) return false;
}
cout << pos1 + 1 << ' ' << pos2 + 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;
}

D

题解

知识点:贪心(贪给我使劲贪)。

结论:跳过以 n1ia[i] 排序的前 k 小值的点,或者说是 i+a[i] 排序的前 k 大值的点。

意识流证明:

n1ia[i] 可以理解为跳过一个点后的代价改变量(i+a[i] ,即 a[i](n1i) 可以理解为跳过一个点的代价减少量),而只要选改变量最小的 k 个点,并且减去在每个跳过的点因为之前跳过点产生的额外代价(跳过的点不应该有代价)共 k(k1)/2 ,可以得到一个最小实际改变量(实际上一定存在一种方法使其小于 0 ,这也说明了最优方法一定把 k 次跳过机会用完),加上改变量就能使总代价最小。

数学证明:微扰法。

假设方案不是跳过最小改变量的 k 个位置,则存在更优方案。

首先,存在某个位置 i 没有被跳过,某个位置 j 被跳过,且有 n1ia[i]<n1ja[j],即 i+a[i]>j+a[j] ,有两种情况i<ji>j

i<j 时,从 a[j]a[i] 会让 ji 个位置代价加一,并且答案减去 a[i] 加上 a[j],又因为 i+a[i]>j+a[j] , 可以得到改变量 a[j]a[i]+ji<0 ,改变一定更优。i>j 的情况同理。

时间复杂度 O(nlogn)

空间复杂度 O(n)

代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;
int a[200007];
bool solve() {
int n, k;
cin >> n >> k;
///显然k个跳点都选上是最小的必要条件
ll ans = -1LL * k * (k - 1) / 2;///每个跳过的点都要减掉之前跳过点产生的额外代价,因为跳过的点没有代价
for (int i = 0;i < n;i++) cin >> a[i], ans += a[i], a[i] = n - 1 - i - a[i];///n-i-a[i]代表跳过这个点相对于不跳过会产生多少代价
sort(a, a + n);///用于挑选前k个产生代价最小的点跳过
for (int i = 0;i < k;i++) ans += a[i];///跳过产生的代价变化
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;
}

E

题解

知识点:数据结构,贪心。

MEX:序列中最小未出现的自然数(包括0)。

例如,MEX({0,1,2,4,5})=3MEX({1,2,4,5})=0

观察DIFFMEX 的结果,发现 MEX 数之前的数必然都出现,则种类数一定和 MEX 一样,因此结果就是大于 MEX 的数的种类数。

我们发现,当通过修改数字使得 MEX 增大时,DIFF 必然是不增的,因此可以先考虑在 k 次操作得到尽可能大的 MEX ,而数组长度为 n ,则必然可以通过修改使在 n 以内的所有 MEX 都能得到,所以可以从 0 开始遍历 ,若存在该数字则继续,否则通过一次操作得到这个数字,这样就可以得到 k 次操作后最大可能的 MEX 了,这样操作得到的 MEX 可能会大于 n 但不需要限制操作次数使其合法,因为大于等于 n 在之后处理中也都能得到正确结果 0

得到最大的 MEX 后,只要考虑操作比 MEX 大的就行(即使可能用不完 k 次),首先因为大于 MEX 的数字种类即是答案,所以优先取完其尽可能多的种类能直接降低答案,其次 MEX 是操作 k 次内的必然结果,无论如何都能在 k 次内达到,而大于 MEX 的数字种类即是答案,那么只考虑 k 次操作后(可能不到 k 次就取完了,剩下的操作可以理解为用小于 MEX 的数补全,从而也能达到 MEX ,但不改变答案 0)这些数的种类即可。特别地,最大 MEX 在取的时候可能大于等于其最大可能值 n ,即最后大于 MEX 数的种类必然为0 ,也即 k 次操作内必然取完大于 MEX 的数,答案也为 0 ,所以在之前的操作不需要通过限制操作次数来限制 MEX

时间复杂度 O(nlog2n)

空间复杂度 O(n)

代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;
bool solve() {
int n, k;
cin >> n >> k;
map<int, int> m;///用于元素与数量映射
for (int i = 0;i < n;i++) {
int tmp;
cin >> tmp;
m[tmp]++;
}
int mex = 0;
for (int i = 0, kk = k;;i++) {
if (!m.count(i)) {
if (kk) kk--;
else {
mex = i;
break;
}
}
}
priority_queue<int, vector<int>, greater<int>> pq;
for (auto i : m) if (i.first > mex) pq.push(i.second);
while (!pq.empty() && k >= pq.top()) { ///答案等于大于mex的元素种类,所以k<最小个数元素时,不用继续,此种类补全k次后不会消失,留着就行
k -= pq.top();
pq.pop();
}
cout << pq.size() << '\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 @   空白菌  阅读(51)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
点击右上角即可分享
微信分享提示