本文已参加「新人创作礼」活动,一起开启掘金创作之路。
这次带来的是 Codeforces Round #792 (Div. 1 + Div. 2) A ~ E 的简单题解。
A. Digit Minimization
l 题意: 现有一个不含 0 的数字。Alice、Bob 两人轮流进行博弈,每一轮博弈如下:
- 首先,Alice 可以交换任意不同两个数位的数字。
- 接下来,Bob 可以删除最后一个数位的数字。
当只剩最后一位数字时游戏结束,Alice 希望最后保留的数字尽可能小,那么最终保留的数字是多少
l 分析: 显而易见的,对于不同数位的数字只存在有三种不同情况:
- 数字长度为1:游戏结束,答案为 n 本身。
- 数字长度为2:由于总是 Alice 先走,故必须交换数字,其次 Bob 删除最后一位数字。那么最后的答案一定是原数字中的最后一位数字,即个位数字。
- 数字长度大于等于3:在最后一轮前,Alice 每次总是将最小的数位放到除最后一位外的任意位置;在最后一轮时,将最小数位移至开头。显而易见的,最后的答案一定是原数字中最小的一位数。
l 代码:
#include<bits/stdc++.h>
#define For(i,j,k) for(int i=j;i<=k;++i)
#define Dow(i,j,k) for(int i=k;i>=j;--i)
#define ll long long
#define pb push_back
#define fir first
#define sec second
#define pb push_back
#define pa pair<int,int>
#define mk make_pair
using namespace std;
inline ll read()
{
ll t=0,dp=1;char c=getchar();
while(!isdigit(c)) {if(c=='-') dp=-1;c=getchar();}
while(isdigit(c)) t=t*10+c-48,c=getchar();
return t*dp;
}
inline void write(ll x){if(x<0) {putchar('-');x=-x;} if(x>=10) write(x/10);putchar(x%10+48);}
inline void writeln(ll x){write(x);puts("");}
inline void write_p(ll x){write(x);putchar(' ');}
int main()
{
int t;
string s;
t=read();
while(t--)
{
cin>>s;
if(s.size()==2) {cout<<s[1]<<endl;continue;}
char a='9';
For(i,0,s.size()-1) a=min(a,s[i]);
cout<<a<<endl;
}
}
B. Z mod X = C
l 题意: 对于 找出合适的
,其中
l 分析: 显而易见的,当 x=a+b+c,y=b+c,z=c 时,一定满足情况(大家可以自己证明一下)
l 代码:
#include<bits/stdc++.h>
#define For(i,j,k) for(int i=j;i<=k;++i)
#define Dow(i,j,k) for(int i=k;i>=j;--i)
#define ll long long
#define pb push_back
#define fir first
#define sec second
#define pb push_back
#define pa pair<int,int>
#define mk make_pair
using namespace std;
inline ll read()
{
ll t=0,dp=1;char c=getchar();
while(!isdigit(c)) {if(c=='-') dp=-1;c=getchar();}
while(isdigit(c)) t=t*10+c-48,c=getchar();
return t*dp;
}
inline void write(ll x){if(x<0) {putchar('-');x=-x;} if(x>=10) write(x/10);putchar(x%10+48);}
inline void writeln(ll x){write(x);puts("");}
inline void write_p(ll x){write(x);putchar(' ');}
int main()
{
int t,a,b,c;
t=read();
while(t--)
{
a=read();
b=read();
c=read();
write_p(a+b+c);
write_p(b+c);
writeln(c);
}
}
C. Column Swapping (fst)
l 题意: 给定若干数组 ,每个数组恰有 m 个元素。
那么能否选择两个位置 ,对于每个数组
交换
,使得交换后,所有数组保持有序。
如果可以,输出这样的位置。否则输出 -1。
l 分析: 首先,让我们检查一下给定的表是否是好的。如果不是,那么有一行的元素应该被替换。
- 假设这一行是 a,b 是排序后的 a 行。然后让我们找到使得 ai≠bi 的位置集 i。
- 如果至少有 3 个这样的位置,那么答案是 -1,因为通过交换,我们最多可以删除 2 个这样的坏位置。
- 如果这样的位置不超过 2 个,那么我们就交换相应的列并检查每一行是否被排序。
- 如果该表是好的,我们就找到了答案。如果不是,那么答案是 -1,因为我们不能对 a 进行排序,之后得到一个好的表格。
l 注意: 不知道是什么原因,下面这段代码 fst 了,Wrong answer on test 13,但实际上,我单独将这个测试点复制出来单独运行,显示的答案是正确的,这很奇怪。
l 代码:
#include<bits/stdc++.h>
#define For(i,j,k) for(int i=j;i<=k;++i)
#define Dow(i,j,k) for(int i=k;i>=j;--i)
#define ll long long
#define pb push_back
#define fir first
#define sec second
#define pb push_back
#define pa pair<int,int>
#define mk make_pair
using namespace std;
inline ll read()
{
ll t=0,dp=1;char c=getchar();
while(!isdigit(c)) {if(c=='-') dp=-1;c=getchar();}
while(isdigit(c)) t=t*10+c-48,c=getchar();
return t*dp;
}
inline void write(ll x){if(x<0) {putchar('-');x=-x;} if(x>=10) write(x/10);putchar(x%10+48);}
inline void writeln(ll x){write(x);puts("");}
inline void write_p(ll x){write(x);putchar(' ');}
#define int long long
vector<int> v[222222],a[222222];
signed main()
{
int t;
t=read();
while(t--)
{
int n,m,flag=0;
n=read();
m=read();
For(i,0,n-1) v[i].resize(m);
For(i,0,m-1) a[i].resize(n);
For(i,0,n-1) For(j,0,m-1) v[i][j]=read();
For(i,0,n-1) For(j,0,m-1) a[j][i]=v[i][j];
sort(a,a+m);
For(i,0,n-1 && !flag)
For(j,1,m-1 && !flag)
if(a[j][i]<a[j-1][i])
{
puts("-1");
flag=1;
}
if(flag) continue;
pair<int,int> ans{-1,-1};
For(i,0,n-1 && !flag)
For(j,0,m-1 && !flag)
if(a[j][i]!=v[i][j])
{
if(ans.first==-1) ans.first=j;
else if(ans.second==-1) ans.second=j;
else if(j!=ans.first && j!=ans.second)
{
puts("-1");
flag=1;
}
}
if(ans.first==-1 && ans.second==-1) puts("1 1");
else {write_p(ans.first+1);writeln(ans.second+1);}
}
}
D. Traps
题意: 现有 n 个陷阱,每个陷阱的代价是 ,你可以选择至多 k 个陷阱,跳过它们(不需要付出代价)。如果选择跳过该陷阱,虽然当前陷阱不需要代价,但其他陷阱的代价均加 1。那么至少需要多少代价,才能按顺序通过所有陷阱?
l 分析: 贪心准则是:选择以下的值较大的跳过:
- 设下标是
,代价是
,优先跳过
较大的陷阱。
该证明来源于 pzr 大佬。
设我们选择了 的位置跳过,则最终答案可以表示为:
l 代码:
#include<bits/stdc++.h>
#define For(i,j,k) for(int i=j;i<=k;++i)
#define Dow(i,j,k) for(int i=k;i>=j;--i)
#define ll long long
#define pb push_back
#define fir first
#define sec second
#define pb push_back
#define pa pair<int,int>
#define mk make_pair
using namespace std;
inline ll read()
{
ll t=0,dp=1;char c=getchar();
while(!isdigit(c)) {if(c=='-') dp=-1;c=getchar();}
while(isdigit(c)) t=t*10+c-48,c=getchar();
return t*dp;
}
inline void write(ll x){if(x<0) {putchar('-');x=-x;} if(x>=10) write(x/10);putchar(x%10+48);}
inline void writeln(ll x){write(x);puts("");}
inline void write_p(ll x){write(x);putchar(' ');}
int a[222222];
int main()
{
int t;
t=read();
while(t--)
{
int n=read(),k=read();
ll sum=0;
For(i,1,n)
{
a[i]=read();
sum+=a[i];
}
sum+=(ll)n*k;
For(i,1,k) sum-=(k-i);
For(i,1,n) a[i]+=i;
sort(a+1,a+n+1,greater<int> ());
For(i,1,k) sum-=a[i];
writeln(sum);
}
}
E. MEX vs DIFF
l 题意: 给定数组,改变至多 k 个元素,使得数组的 DIFF - MEX 最小。
- MEX 是最小的没有出现在数组中的正整数元素。
- DIFF 是数组中元素的个数(值相同的元素仅计数一次)。
l 分析: 考虑所有操作后可能的 MEX,按照递增的顺序检查它们。
-
现在让我们固定一些 MEX=m。数组中应该有从 0 到 m 的所有数字,所以数组中有些 "洞" 应该被覆盖。洞是指从 0 到 m 的整数,它不存在于数组中。如果最后至少有一个洞,就不可能得到 MEX=m。
-
现在让我们来看看我们应该如何覆盖这些洞。首先,我们需要使用大于 m 的整数。显而易见的,使用这些整数总是不比从 0 到 m 的整数差。这是因为每次我们覆盖一个洞时,MEX 至少增加一个(我们以增加的顺序覆盖洞),DIFF 的值最多增加 1,当我们改变最后一个相同的元素时,它不会增加。
-
之后,如果我们使用了所有大于 m 的整数,我们应该使用那些从 0 到 m 的整数,但只使用那些出现一次以上的整数。
-
通过这些操作,我们至少使 MEX 增加了 1,并使 DIFF 正好增加了 1(因为我们覆盖了一个洞)。
-
现在我们注意到,当按照递增的顺序考虑每个 MEX 值时,我们可以简单地保持一些关于数组当前状态的信息:
- 一个帮助我们找到大于m的元素在数组中出现次数较少的集合
- 未覆盖的洞的数量
- 从 0 到 m 的 "奖励" 元素的数量(从 0 到 m 的整数减去从 0 到 m 的那些元素的 DIFF),并且很容易看到当我们增加 MEX 时,它是如何改变的。
-
所以总的来说,我们可以计算出从 0 到 n 的所有 MEX 的答案。
l 代码:
#include<bits/stdc++.h>
#define For(i,j,k) for(int i=j;i<=k;++i)
#define Dow(i,j,k) for(int i=k;i>=j;--i)
#define ll long long
#define pb push_back
#define fir first
#define sec second
#define pb push_back
#define pa pair<int,int>
#define mk make_pair
using namespace std;
inline ll read()
{
ll t=0,dp=1;char c=getchar();
while(!isdigit(c)) {if(c=='-') dp=-1;c=getchar();}
while(isdigit(c)) t=t*10+c-48,c=getchar();
return t*dp;
}
inline void write(ll x){if(x<0) {putchar('-');x=-x;} if(x>=10) write(x/10);putchar(x%10+48);}
inline void writeln(ll x){write(x);puts("");}
inline void write_p(ll x){write(x);putchar(' ');}
int a[222222],vis[222222];
int main()
{
int t;
t=read();
while(t--)
{
int n=read(),k=read();
map<int,int> mp;
For(i,1,n)
{
int x=read();
mp[x]++;
}
int cnt=k,mex=n;
For(i,0,n-1)
if(!mp[i])
{
if(cnt) cnt--;
else
{
mex=i;
break;
}
}
set<pair<int,int>> s;
for(auto [x,y]:mp) if(x>mex) s.insert({y,x});
while(s.size() && k>=(s.begin()->first))
{
k-=s.begin()->first;
s.erase(s.begin());
}
writeln(s.size());
}
}
F~H
这真的好难,我根本不会诶。