Codeforces Round #792 (Div. 1 + Div. 2)
Codeforces Round #792 (Div. 1 + Div. 2)
A
题意
给定一个数 \(n\) 每次 按次序 执行两个操作。
操作1:选择两个不同位置的数字并交换
操作2:删除最后一个数
重复,直到只剩下最后一个数字。问最后剩下的最小是多少。
思路
\(n\) 位数小于3时,显然是第一个数字。
\(n\) 位数大于等于3时,我们总可以在将最小数字 \(mn\) 删除前将之前移。因此总可以把 \(mn\) 换到最前
#include<bits/stdc++.h>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define ull unsigned long long
#define endl '\n'
#define int long long
using namespace std;
mt19937 mrand(random_device{}());
int rnd(int x) { return mrand() % x;}
typedef pair<int,int> PII;
const int MAXN =10 + 2e5 ,mod=1e9 + 7;
void solve()
{
string s; cin >> s;
if(s.size() == 2) {
cout << s.back() << "\n";
}else {
char mn = 100;
for(int i = 0;i < s.size();i ++) if(mn > s[i]) mn = s[i];
cout << mn << "\n";
}
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
int T;cin>>T;
while(T--)
solve();
return 0;
}
B
题意
给三个正整数 \(a~,b~,c\) ,求 \(x~,y~,z\) 满足以下方程。
思路
一开始想试着硬解这个方程,最后败给了垃圾的数学功底···
观察这个式子的形式,可以成一个环,所以只要找到 \(x~,y~,z\) 中任意一个值,其他两都可以推出,那么不妨令 \(z = c\),易得 $ y = z + b\(,\)x = y + a$
#include<bits/stdc++.h>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define ull unsigned long long
#define endl '\n'
#define int long long
using namespace std;
mt19937 mrand(random_device{}());
int rnd(int x) { return mrand() % x;}
typedef pair<int,int> PII;
const int MAXN =10 + 2e5 ,mod=1e9 + 7;
void solve()
{
int a,b,c; cin >> a >> b >> c;
int z = c;
int y = z + b;
int x = y + a;
cout << x << " " << y << " " << z << "\n";
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
int T;cin>>T;
while(T--)
solve();
return 0;
}
C
题意
给一个 \(n*m\) 的网格,可以交换两列(两列不一定相同),求是否可以使得整个网格单调不减
网格单调不减指的是对任意行 \(i\) ,其中任意的 \(j < k\) ,都有 $ x_{(i,j)} - x_{(i,k)} <= 0$
思路
思维模拟
首先我们要知道,一次交换最多只能让两个元素排好序,那么,对第一个不单调的行,由于我们只可以进行一次交换,所以找到所有不在正确位置上的元素,如果个数等于2,就交换这两列,判断是否有序,如果大于2,显然就不行了(不会出现只有一个元素没在正确位置的情况)。
#include<bits/stdc++.h>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define ull unsigned long long
#define endl '\n'
#define int long long
using namespace std;
mt19937 mrand(random_device{}());
int rnd(int x) { return mrand() % x;}
typedef pair<int,int> PII;
const int MAXN =10 + 2e5 ,mod=1e9 + 7;
void solve()
{
int N,M; cin >> N >> M;
vector<vector<int>> a(N + 1,vector<int>(M + 1));
vector<bool> st(M + 1);
for(int i = 1;i <= N;i ++) for(int j = 1;j <= M;j ++) {
cin >> a[i][j];
}
int x = -1,y = -1;
for(int i = 1;i <= N;i ++) {
if(is_sorted(a[i].begin() + 1, a[i].end())) continue;
vector<int> t = a[i];
sort(t.begin() + 1, t.end());
for(int j = 1;j <= M;j ++) if(t[j] != a[i][j]) {
if(x == -1) x = j;
else if(y == -1) y = j;
else {
cout << "-1\n";
return;
}
}
if(x != -1 && y != -1) break;
}
if(x != -1 && y != -1) {
for(int i = 1;i <= N;i ++) swap(a[i][x], a[i][y]);
}
for(int i = 1;i <= N;i ++) {
if(!is_sorted(a[i].begin() + 1,a[i].end())) {
cout << -1 << endl;
return;
}
}
if(x == -1 && y == -1) x = y = 1;
cout << x << " " << y << endl;
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
int T;cin>>T;
while(T--)
solve();
return 0;
}
D
题意
有 \(n\) 个陷阱,第 \(i\) 陷阱伤害 \(a_i\) ,有 \(k\) 次机会跳过一个陷阱,但会使得之后所有的陷阱多产生 \(1\) 的额外伤害
最小化受到的伤害
思路
贪心
dp时间复杂度太大,开始考虑贪心。
单看跳过第 $i $ 个陷阱,对答案的贡献是 $a_i - (n - i) $ ,猜测贪心的选择贡献前 \(k\) 大的陷阱跳过。
证明以后补上
#include<bits/stdc++.h>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define ull unsigned long long
#define endl '\n'
#define int long long
using namespace std;
mt19937 mrand(random_device{}());
int rnd(int x) { return mrand() % x;}
typedef pair<int,int> PII;
const int MAXN =10 + 2e5 ,mod=1e9 + 7;
void solve()
{
int N,K; cin >> N >> K;
vector<int> a(N + 1);
for(int i = 1;i <= N;i ++) cin >> a[i];
vector<PII> b(N + 1);
for(int i = 1;i <= N;i ++) {
b[i].first = a[i] - (N - i);
b[i].second = i;
}
sort(b.begin() + 1,b.end());
for(int i = N;i > N - K;i --) {
auto [xx,id] = b[i];
a[id] = 0;
}
int ans = 0;
for(int i = 1, cnt = 0;i <= N;i ++) {
if(!a[i]) cnt ++;
else ans += a[i] + cnt;
}
cout << ans << endl;
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
int T;cin>>T;
while(T--)
solve();
return 0;
}
E
题意
对一个长为 \(n\) 的序列 \(a\),有 \(k\) 次机会修改任意一个数, 求 \(diff(a) - mex(a)\) 的最小值。
- \(diff(a)\) :序列中数字的种类数
- \(mex(a)\):序列的 \(mex\)
思路
贪心,贪 \(mex\) 最大
首先可以感觉到增加 \(mex\) 比减小 \(diff\) 更加容易。
再做一个分类讨论
假如我们现在确定了一个 \(mex\) ,那么比 \(mex\) 小的数要全部存在,于是要填一些 空隙 ,如,\(mex = 4\) 时 ,\([0,1,3,5]\) 在 \(2\) 的位置就有一个 空隙
然后,如果我们用比 \(mex\) 小的去填,会发现对答案没有任何贡献,一定只会令 \(diff + 1\) ,同时 $mex +1 $
那空隙只能用比 \(mex\) 大的去填。
确定贪心策略,我们尽可能最大化 \(mex\) ,之后再尝试减小 \(diff\) 。
不太好说明,具体看代码
#include<bits/stdc++.h>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define ull unsigned long long
#define endl '\n'
#define int long long
using namespace std;
mt19937 mrand(random_device{}());
int rnd(int x) { return mrand() % x;}
typedef pair<int,int> PII;
const int MAXN =10 + 2e5 ,mod=1e9 + 7;
void solve()
{
int N,K; cin >> N >> K;
vector<int> a(N + 1);
map<int,int> mp;
for(int i = 1;i <= N;i ++) cin >> a[i], mp[a[i]] ++;
int mex = 0;
int rem = K;
vector<int> v;
for(auto [x,c] : mp) { // 求最大的 mex 当有一个空隙往里插一个值。
while(rem && mex < x) mex ++, rem --;
if(x == mex) mex ++;
else v.emplace_back(c);
}
sort(v.begin(),v.end());
int ans = v.size();// ------- mex xxxxxx
for(auto c : v) { // 求最小 diff
if(K >= c) {
K -= c;
ans --;
}
}
cout << ans << endl;
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
int T;cin>>T;
while(T--)
solve();
return 0;
}