比赛链接:Dashboard - Codeforces Round 960 (Div. 2) - Codeforces
A题 Submission Bait
题目:
中文题面:
爱丽丝和鲍勃在大小为 n 的数组 a 中进行游戏。
他们轮流进行运算,爱丽丝先开始。不会运算的一方将输掉比赛。一开始,变量 mx 被设置为 0 。
在一次操作中,玩家可以
- 选择一个索引 i ( 1<=i<=n )。 使得 ai>=mx ,并将 mx 设置为 ai 。然后将ai设为0 。
判断爱丽丝是否有一个获胜的策略。
输入
第一行包含整数 t( 1 <= t <= 10^3 )—测试用例的数量。
对于每个测试用例:
-第一行包含整数 n( 2 <= n <= 50 )-数组的大小。
-第二行包含 n 整数 a1, a2, ..., an( 1<=ai<=n )-数组的元素。
输出
对于每个测试用例,如果 Alice 的策略获胜,则输出 "YES"。否则,输出 "NO"。
可以用任何大小写(大写或小写)输出答案。例如,字符串 "yEs"、"yes"、"Yes "和 "YES "将被识别为肯定回答。
注
在第一个测试案例中,爱丽丝可以选择 i=1 ,因为 a1=2 >= mx=0 。
爱丽丝操作后, a=[0,1] 和 mx=2 。鲍勃无法进行任何操作。爱丽丝获胜。
在第二个测试案例中,爱丽丝没有获胜策略。
例如,如果爱丽丝选择了 i=1 ,那么在爱丽丝的操作之后, a=[0,1] 和 mx=1 。那么,鲍勃可以选择 i=2 ,因为 a2=1>=mx=1 。鲍勃操作后 a=[0,0] 和 mx=1 。爱丽丝无法进行任何操作。鲍勃获胜。
解题思路:
由样例来看,我们从数组中最大的数来看,如果最大的数的个数是奇数个,那么必赢,因为,爱丽丝先拿一个最大的数,由于ai>=mx 条件限制,鲍勃也必然拿一个最大的数,最大的数的个数是奇数个,最后一个最大的数必然被爱丽丝拿完,获胜。如果是偶数个,爱丽丝也不必然输,看第二大的数的个数,如果是偶数,那么爱丽丝也是获胜。比如最大数的个数为2个,第二大的个数为3个,爱丽丝先拿第二大的数,鲍勃拿第二大的数,爱丽丝再拿第二大的数,由于条件限制鲍勃只能拿最大的数,最大的数还剩余1个最后被爱丽丝拿走,爱丽丝获胜。由此我们可知,只要数组中有一个数的个数为奇数个那么爱丽丝必然胜利,否则则输。
AC代码:
#include<iostream> #include<cmath> #include<algorithm> using namespace std; const int N=55; int a[N]; int t,n,m; bool flag; int main(){ cin>>t; while(t--){ cin>>n; flag=0; for(int i=1;i<=n;i++)cin>>a[i]; sort(a+1,a+n+1); for(int i=n;i>=1;i--){ int len=upper_bound(a+1,a+n+1,a[i])-lower_bound(a+1,a+n+1,a[i]);//计算此数的个数 if(len%2==1){//如果是奇数个数必赢 flag=1; break; } } //如果都是偶数个数必输 if(flag==1){ cout<<"YES"<<endl; }else{ cout<<"NO"<<endl; } } return 0; }
B题 Array Craft
题目:
中文题面:
输入
第一行包含一个整数 t ( 1 <= t <= 10^4 ) - 测试用例的数量。
对于每个测试用例
- 唯一一行包含三个整数 n 、x 和 y( 2 <= n <= 10^5, 1 <= y < x <= n) .
保证所有测试用例中 n 的总和不超过 10^5 。
输出
对于每个测试用例,在新的一行中输出 n 空格分隔的整数 a1, a2, ..., an。
注
解题思路:
由题意可知,最大前缀和跟最大后缀和的下标都应该置为1,因为必然走到这个点,如果是-1,那么总和会减小,必然不走。题中最大前缀和下标大于最大后缀和下标,说明两者有重合的部分,这一部分都是必然走的,总和一定是大于0的,不妨我们把它们都置为1,再分为[1,y-1]、[x+1,n],这两个区间对总和起副作用,一定是小于0的,我们按照 -1 1的顺序给其赋值,如果是偶数个,那么总和为0不起作用,如果是奇数个,总和为-1,也可以满足条件。
综上所述,可以分为三个区间
AC代码:
#include<iostream> using namespace std; const int N=1e5+5; int t,n,x,y; int a[N]; int main(){ cin>>t; while(t--){ cin>>n>>x>>y; for(int i=x+1;i<=n;i+=2)a[i]=-1;//[x+1,n]置 -1 1 for(int i=x+2;i<=n;i+=2)a[i]=1; for(int i=y-1;i>=1;i-=2)a[i]=-1;//[1,y-1]置 -1 1 for(int i=y-2;i>=1;i-=2)a[i]=1; for(int i=y;i<=x;i++)a[i]=1;//[y,x]置 1 for(int i=1;i<=n;i++)cout<<a[i]<<" "; cout<<endl; } return 0; }
C题 Mad MAD Sum
题目:
解题思路:
经过对样例的分析,我们发现b数组是单调递增的。而且只有出现两个重复的的数MAD才有意义,样例中a=2 2 3 一轮过后, a=0 2 2 两轮过后 a=0 0 2 三轮过后 a=0 0 0,我们发现数组具有右移的特征。我们先MAD模拟一轮后,不断去掉单个数字,右移,加和得到答案。
AC代码:
#include<iostream> #include<cstring> using namespace std; typedef long long ll; const int N=2e5+5; ll n,t,a,b,c,sum; int p1[N],p2[N]; int main() { cin>>t; while(t--) { cin>>n; sum=a=b=c=0; memset(p1,0,sizeof(p1)); memset(p2,0,sizeof(p2)); while(n--) { cin>>a; if(p1[a]) b=max(b,a); else p1[a]=1; if(p2[b]) c=max(c,b); else if(b) p2[b]=1; sum+=a+b+(n+1)*c; } cout<<sum<<endl; } return 0; }
D题 Grid Puzzle
题目:
您将得到一个大小为 n 的数组 a 。
有一个 n*n 网格。
在第 i 行中,第一个 ai 单元格为黑色,其他单元格为白色。换句话说,注意 (i,j) 作为第 i行和第 j 列中的单元格,单元格 (i,1), (i,2), ..., (i,ai)为黑色。并且单元格(i,ai+1), ..., (i,n) 是白色的。您可以按任意顺序多次执行以下操作:
-将 2 * 2 网格染成白色
-把整排都染成白色。
请注意,您不可以将整个列染成白色。
找出将所有单元格染成白色所需的最少操作次数。
解题思路:
经过对样例的分析,我们可以知道这个题要根据此行有多少个黑色格子来选择使用操作几,我们从样例分析来看,当一行中黑色格子大于等于5个的时候,操作二就更优了,因为如果大于等于五个黑色格子,那么只少要用三个操作一才能满足考虑对上一行跟下一行的影响,如果上一行跟下一行只有小于等于2个黑色格子,那么可以满足,如果都大于2个,那么还需要一次操作一,这无非增加了次数。剩下的只用操作一解决即可。还一种特殊情况当上一行为都是白色时,且3<=a<=4的格子数,用操作二比较好,这一点在样例9中就有体现。
其次还有一点需要注意,操作一分左右两个操作一,可以看这组样例
3 1 3 1
很明显,右边的操作一对下一行不起作用,那么我们就要进行分类讨论了,如果是这一行的左边操作一,对下一行一定有用(除空行),如果是右边操作一,那么可以有用,当下一行黑色格子数大于等于3就起作用了。
AC代码:
#include<iostream> #define int long long using namespace std; const int N=2e5+5; int n,T; int a[N]; signed main(){ ios::sync_with_stdio(0); cin>>T; while(T--){ cin>>n; for(int i=1;i<=n;i++)cin>>a[i]; int flag=0,ans=0;//flag表示进行操作几 ans表示答案 //flag 0没有操作、1靠左边操作1、-1靠右边操作1、2操作2 for(int i=1;i<=n;i++){ if(a[i]==0){//如果全是白色,不需要操作 flag=0; }else if(a[i]<=2){//1--2个黑色 if(flag==1){//上一行进行了一次操作1 flag=0; }else ans++,flag=1;//进行一次操作1 }else if(a[i]<=4){//3--4个黑色 if(flag==0)ans++,flag=2;//上一行没有操作,进行一次操作2 else if(flag==1)ans++,flag=-1;//上一行进行了操作1(左边),还需进行一次(右边) else if(flag==-1)ans++,flag=1;//上一行进行了操作1(右边),还需进行一次(左边) else ans++,flag=2; } else ans++,flag=2;//更多的话进行操作2更优 } cout<<ans<<endl; } return 0; }
剩余题目待更中......