Codeforces Round #767 (Div. 2) 补题题解(前五题)


官方题解


记录第三次比赛的题解

第一题过得很快,第二题编译器崩了,心态也崩了,但自己想的解法是和1e9有关的,应该意识到不是正解,以后比赛还是要更多想

比赛传送门


A. Download More RAM

原题传送门
思路不难,本来昨晚交的时候过了,后来又被卡掉了,原因是写的cmp函数多加了一个限制,以后写子函数应该考虑所有情况的返回值,虽然平台把题卡掉了,但是又把分算上了,也是神奇

#include<bits/stdc++.h>
 
using namespace std;
 
typedef long long ll;
typedef pair<int,int>PII;
 
const int N = 110;
 
PII a[N];
 
bool cmp(PII a, PII b){
	return a.first < b.first;//划重点,要考虑所有情况的返回值
}
 
int main()
{
	int T;
	cin>>T;
	while(T -- ){
		int n, k;
		cin>>n>>k;
		for(int i = 0; i < n; i ++ ){
			cin>>a[i].first;
		}
		
		for(int i = 0; i < n; i ++ ){
			cin>>a[i].second;
		}
		
		sort(a, a + n, cmp);
		
		
		for(int i = 0; i < n; i ++ ){
			if(k >= a[i].first){
				k += a[i].second;
			}
		}
		
		cout<<k<<endl;
	}
	return 0;
}

B. GCD Arrays

原题传送门
写这道题的时候一直在死脑筋的考虑1e9的输入和判断,就和质数过不去,当想到这个解法的时候,就应该意识到不对了,以后要注意思考出的方法的可行性
本题的思维:连续区间内,相邻的奇偶数的最大公约数一定为1,每次操作可以将一对奇偶数相乘,消去一个奇数,所以如果操作数大于奇数个数,就可以达成目标,反之不能,l==r时需要特判一下是否为1

#include<bits/stdc++.h>

using namespace std;

int main()

{
	int T;
	cin>>T;
	while(T -- ){
		int l, r, k;
		cin>>l>>r>>k;
		if(l == r){
			if(l == 1) cout<<"NO"<<endl;
			else cout<<"YES"<<endl;
		} 
		else{
			int x = (r + 1) / 2 - l / 2;
			if(k >= x) cout<<"YES"<<endl;
			else cout<<"NO"<<endl;
		}
	}
	return 0;
}

C. Meximum Array

原题传送门
利用贪心找到前k个数字里最小没出现的非负数(MEX),利用贪心的方法不断更新序列和MEX,具体思路见代码
学到的:vector比数组快、set的使用(不重复且默认升序排列元素的容器

#include<bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	int T;
	cin>>T;
	while(T -- ){
		int n;
		cin>>n;
		/*vector比数组要快*/
		vector<int>a(n + 1);
		vector<int>b(n + 1);
		for(int i = 1; i <= n; i ++ ){
			cin>>a[i];
			b[a[i]] ++ ;
		}
		int mex = n + 1;
		for(int i = 0; i <= n; i ++ ){
			if(b[i] == 0){//找到数组中没出现过的最小非负整数 
				mex = i;//记录这个数 
				break;
			} 
		}
		vector<int>ans;
		set<int>s;//内部自动有序且不含重复元素的容器 
		for(int i = 1; i <= n; i ++ ){
			if(s.size() == 0){//当s为空时,代表刚进入或者一个贪心形成的序列已经完成找到MEX,可以寻找下一个MEX 
				ans.push_back(mex);//把第一个没出现过的最小非负整数加入ans 
				for(int i = 0; i < mex; i ++ ){
					s.insert(i);
				}
			}
			//按顺序遍历arr,当s中的数清空了,就代表可以找出一组MEX,而MEX通过更新一直记录的是后面数组没有出现的最小非负整数 
			b[a[i]] -- ;//从数组a中删除这个数 
			if(s.find(a[i]) != s.end()) s.erase(a[i]);//如果当前便利到的数组中的数不是s中最后一个数,就把这个数删去
		    /*set的特性,只要在set中存在的数,s.find(arr[i])!=s.end()都成立,所以 
			这一步的作用是删除前k位中所有小于mex的数,当s为空时,代表当前遍历过的序列的MEX即是b数组的第一个数 
			*/ 
			if(b[a[i]] == 0) mex = min(mex, a[i]);//更新从i往后序列中没出现过的最小非负整数 
		}
		
		cout<<ans.size()<<endl;
		for(int i = 0; i < ans.size(); i ++ ){
			cout<<ans[i]<<" ";
		}
		cout<<endl;
	}
	return 0;
}

D. Peculiar Movie Preferences

原题传送门
将字符串转化为long long的数储存在向量内,这个方法妙啊
具体解法见代码及解释

#include<bits/stdc++.h>

using namespace std;

#define v vector

typedef long long ll;

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	int T;
	cin>>T;
	while(T -- ){
		int n;
		cin>>n;
		int ans = 0;
		v<string>s(n);
		for(int i = 0; i < n; i ++ ) cin>>s[i];//循环读入字符串 
		
		v<ll>f2(27 * 27);//记录长度为2的字符串 
		v<ll>f3(27 * 27);//记录长度为3的字符串的前两位,因为只要前两位反转和一个长度为2的字符串相反,则这两个字符串也能形成回文字符串 
		v<ll>h3(27 * 27 * 27);//记录长度为3的字符串 
		for(auto t : s){
			string x = t;
			string r = t;
			reverse(x.begin(), x.end());
			if(x == t){//如果自己就是个回文字符串 
				ans = 1;
				break;
			}
			int l = t.size();
			if(l == 2){
				ll r1 = (t[0] - 'a') + (t[1] - 'a') * 27;//正序查找,将两位的字符串反转,转化为long long  
				if(f2[r1] || f3[r1]){
					ans = 1;
					break;
				}
				f2[(t[0] - 'a') * 27 + (t[1] - 'a')] = 1;//逆序记录这个字符串 
			}else{//字符串长度为3的情况 
				ll r1 = (t[2] - 'a') * 27 + (t[1] - 'a');
				ll r2 = (t[0] - 'a') + (t[1] - 'a') * 27 + (t[2] - 'a') * 27 * 27;
				if(f2[r1] || h3[r2]){
					ans = 1;
					break;
				}
				f3[(t[1] - 'a') + (t[0] - 'a') * 27] = 1;//记录字符串前两位用于判断 
				h3[(t[0] - 'a') * 27 * 27 + (t[1] - 'a') * 27 + (t[2] - 'a')] = 1;//记录字符串三位用于判断 
			}
		}
		if(ans) cout<<"YES"<<endl;
		else cout<<"NO"<<endl;
	}
	return 0;
	
}

E. Grid Xor

原题传送门

题意:
一个 n × n 的方阵每个位置有一个数, n是偶数.但是我们现在只知道和每个位置相邻的四个位置上的数的异或值,求整个方阵里所有数的异或

借用大佬的题解
首先我们画图,发现如果选择一个位置,相当于点亮它四周的灯,那我们的目的就是把整个方阵全部点亮,并且每个位置只点一次.那么当我们选择相邻的两个位置的时候,所点亮的位置刚好是不重复的,我们把选择相邻的两个方格所点亮的格子称为一块拼图.那么只要用这样的拼图把整张图完全覆盖就可以了.点亮的过程刚好可以贪心,只要一个位置能和下面的方格或者右边的方格形成一块没有使用过的拼图,我们就直接贪心去拼就可以了
题解出处
代码:

#include<bits/stdc++.h>

using namespace std;

typedef long long ll; 

const int N = 1010;

int n;
int a[N][N];
bool vis[N][N];

bool Is(int x, int y, int p){
	if(x < 0 || x > n || y < 0 || y > n) return 1;//在边界外的地盘不影响结果,所以可以直接返回1 
	if(p) return vis[x][y] = 1;//当输入的p为1时,表示要点亮这块地盘,点亮并标记 
	//当p为0时,代表检查这块地盘是否被标记,如果vis[x][y]为1,代表这个格子已经被点亮过返回0表示这块地盘不能用,反之返回1表示可以用  
	return vis[x][y] ? 0 : 1;
}

bool check(int x, int y, int p = 0){//检验x,y周围的四块地盘有没有被点亮 
	//只有当这四个格子都在界外或没有被点亮才能return 1,代表着这块地盘可用 
	return Is(x - 1, y , p) && Is(x + 1, y, p) && Is(x, y + 1, p) && Is(x, y - 1, p);
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	int T;
	cin>>T;
	while(T -- ){
		cin>>n;
		
		//初始化每一块都没有被照亮 
		for(int i = 0; i <= n; i ++ ){
			for(int j = 0; j <= n; j ++ ){
				vis[i][j] = 0;
			}
		}
		
		//读入数据 
		for(int i = 1; i <= n; i ++ ){
			for(int j = 1; j <= n; j ++ ){
				cin>>a[i][j];
			}
		}		
		
		int ans = 0;//记录答案 
		
		for(int i = 1; i <= n; i ++ ){
			for(int j = 1; j <= n; j ++ ){
				if(j < n && check(i, j) && check(i, j + 1)){//如果这一块还没出界且相邻两块的相邻四块都没有被点亮 
					//如果这两块相邻的都没有被标记,就点亮他们并标记他们已被点亮 
					check(i, j, 1);
					check(i, j + 1, 1);
					ans ^= a[i][j]^a[i][j + 1]; 
				}
				if(i < n && check(i + 1, j) && check(i, j )){//如果这一块还没出界且相邻两块的相邻四块都没有被点亮 
					//如果这两块相邻的都没有被标记,就点亮他们并标记他们已被点亮 
					check(i + 1, j, 1);
					check(i, j, 1);
					ans ^= a[i + 1][j]^a[i][j]; //这两个值代表这两块相邻地盘分别相邻的四块之间的异或和,刚好是八块不重复的地盘 
				}				
			}
		}
		cout<<ans<<endl;
	}
	return 0;
}

总结

后面的题因为水平有限就先不补了,有时间再研究 总结来说,这次补题收获还挺大,没有枉费我花了一天时间补了五题,下一场希望有进步吧,注意思维和方法的可行性,注意心态,要有紧迫感,这一场编译器崩了是真的搞人心态...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值