【codeforces】【比赛题解】#864 CF Round #436 (Div.2)

时间:2021-05-30 13:51:01

做出了4题,还不错,可惜还是掉rating……能保持在蓝名已经不错了。

题目跳转链接

【A】公平的游戏

题意:

Petya和Vasya在玩游戏。他们有n张卡片(n是偶数)。每张卡片上有一个整数。

游戏开始前,Petya会选出一个数,然后Vasya会选出另一个数(不同于Petya选的数),然后每人从卡片中抽出所有写着他选中的数的卡片。

举个例子,Petya选了5,而Vasya选了10,那么Petya抽出所有写着5的卡片,而Vasya抽出所有写了10的卡片。

如果Petya和Vasya能抽走所有卡片,并且Petya和Vasya获得的卡片数量相同,那么这个游戏是公平的。

判断这个游戏是否公平!

输入:

第一行,一个数\(n\,(2\leqslant n\leqslant100)\)。

接下来n行包含了一个整数序列\(a_{1},a_{2},\cdots,a_{n}\)(每行一个数,\(1\leqslant a_{i}\leqslant 100\))。

输出:

如果游戏是公平的,输出"YES",并在下一行输出两个数,表示Petya和Vasya选的两个数,能让这个游戏公平。

如果游戏不是公平的,输出"NO"。

样例:样例见原题。

题解:

水题。看懂了题意就会做了。要求在序列中只有两种不同的数,并且这两种数的数量相同。

 1 #include<cstdio>
2 int num[1000],n;
3 int main(){
4 scanf("%d",&n);
5 int x;
6 for(int i=1;i<=n;++i) scanf("%d",&x), num[x]++;
7 int ns=0,a1=0,a2=0;
8 for(int i=1;i<=100;++i){
9 if(num[i]){
10 ++ns;
11 if(!a1) a1=i;
12 else a2=i;
13 }
14 if(ns>2){puts("NO");return 0;}
15 }
16 if(ns==2){
17 if(num[a1]==num[a2]) printf("YES\n%d %d",a1,a2);
18 else puts("NO");
19 }
20 else puts("NO");
21 return 0;
22 }

【B】Polycrap和字母

题意:

Polycrap喜爱小写字母,而讨厌大写字母。他拿到了一个字符串\(s\),只包含小写和大写字母。

他想从这个字符串中选出一个最大的位置的集合\(A\),使这个集合满足:

①在\(s\)中\(A\)中的元素位的字符互不相同且都为小写字母。

②这些位置之间在s中没有一个是大写字母,即对于任意\(a_{1}<j<a_{2}\),\(s[j]\)都不为大写字母(\(a_{1},a_{2}\in A\))。

告诉Polycrap这个集合的大小。

输入:

第一行,一个整数\(n,\,(1\leqslant n\leqslant 200)\),表示s的长度。

第二行,字符串s,长度为n,只包含大写和小写字母。

输出:

一个数,表示集合的大小。

样例:样例见原题。

题解:

仔细分析题意,可以得出:要求的是任意两个相邻的大写字母之间的不同的小写字母的最大个数。

#include<cstdio>
int n,ans=0;
char str[9999];
int vis[2222];
int main(){
scanf(
"%d",&n);
scanf(
"%s",str); str[n]='A'; str[n+1]='\0';
for(int i=0;i<=n;++i){
if('a'<=str[i]&&str[i]<='z'){
vis[str[i]]
=1;
}
else{
int sum=0;
for(int j='a';j<='z';++j)
if(vis[j]) ++sum;
if(ans<sum) ans=sum;
for(int j='a';j<='z';++j)
vis[j]
=0;
}
}
printf(
"%d",ans);
return 0;
}

【C】公共汽车

题意:

一辆公共汽车在\(x\)轴上运动,从\(x=0\)运动到\(x=a\)。当它运动到\(x=a\)时,就立刻掉头前往\(x=0\),当它到达\(x=0\)时,又立刻掉头前往\(x=a\),如此往复。

从\(x=0\)运动到\(x=a\)或者从\(x=a\)运动到\(x=0\)的过程叫做一次旅程。公共汽车总共要行驶\(k\)次旅程。

它的油箱可以装\(b\)升汽油,每运动一个单位要消耗一升的汽油,公共汽车开始时是满油的。

在\(x=f\)处有一个加油站\((0\leqslant f\leqslant a)\),没有其他的加油站了。当从任一方向经过加油站时,公共汽车都可以停下来并加油,装满它的油箱。

公共汽车为了行驶\(k\)次旅程,最少需要加多少次油呢?它最开始从\(x=0\)处开始运动。

输入:

四个数,\(a,b,f,k\)。

输出:

一个数,表示最少加多少次油。

样例:样例见原题。

题解:

很简单的贪心,没油了就加油,只有一点点细节。

 1 #include<cstdio>
2 int a,b,f,k,ans;
3 int main(){
4 scanf("%d%d%d%d",&a,&b,&f,&k);
5 int now=b,dir=0;
6 if(now<f||now<a-f){puts("-1");return 0;}
7 now-=f;
8 for(int i=1;i<k;++i){
9 if(dir==0){
10 if(now<a+a-f-f) ++ans,now=b;
11 now-=a+a-f-f;
12 if(now<0){puts("-1");return 0;}
13 }
14 if(dir==1){
15 if(now<f+f) ++ans,now=b;
16 now-=f+f;
17 if(now<0){puts("-1");return 0;}
18 }
19 dir^=1;
20 }
21 if(dir==0){
22 if(now<a-f) ++ans;
23 }
24 if(dir==1){
25 if(now<f) ++ans;
26 }
27 printf("%d",ans);
28 return 0;
29 }

【D】构造全排列

题意:

Ivan有一个大小为\(n\)的数组,其中每个元素都是\(1\)到\(n\)之间的整数。

最近Ivan学习了关于全排列以及它们的字典序的有关知识。现在他想要替换最少的元素,使得他的数组变成一个全排列。

在替换元素最少的情况下,Ivan想要最终获得的全排列的字典序尽量小。

注意,替换的元素最少是第一关键的。

输入:

第一行,一个数\(n\;(2\leqslant n\leqslant200\,000)\)。

第二行,\(n\)个数,\(a_{1},\,a_{2},\,\cdots,\,a_{n}\,(1\leqslant a_{i}\leqslant n)\),表示Ivan的数组。

输出:

第一行,一个数\(q\),表示最少要替换的次数。

第二行,输出在\(q\)次内能得到的字典序最小的全排列。

样例:样例见原题。

题解:

可以证明,\(q\)等于数组中所有数出现次数-1的和。

 我们也可以算出某一个数出现的次数,以判断它是否需要替换。

由此可以想到按照顺序的贪心。

具体看代码吧。

 1 #include<cstdio>
2 int n,a[200001],num[200001],k[200001],tbd[200001],tot=0;
3 int main(){
4 scanf("%d",&n);
5 for(int i=1;i<=n;++i) scanf("%d",a+i), ++num[a[i]];
6 for(int i=1;i<=n;++i)
7 if(!num[i]) tbd[++tot]=i;
8 printf("%d\n",tot);
9 int t=1, left=tot;
10 for(int i=1;i<=n;++i){
11 if(num[a[i]]>1){
12 // printf("%d %d %d\n",a[i],tbd[t],k[a[i]]);
13 if(a[i]>tbd[t]) num[a[i]]--, a[i]=tbd[t++], --left;
14 else if(left==tot-t+1&&k[a[i]]) num[a[i]]--, a[i]=tbd[t++], --left;
15 else k[a[i]]=1;
16 }
17 printf("%d ",a[i]);
18 }
19 return 0;
20 }

【E】火灾

题意:

不会的啊……XD