十六届蓝桥杯省赛C++A组A-F

题目文件链接 解压密码 65Tg7d9d

题解仅供参考,黑客那道在洛谷T一个点

A: 寻找质数

本题总分:5分

【问题描述】 如果一个正整数只能被1和它本身两个数整除,就称为一个质数。最小的 几个质数依次是2,3,5,7,11,13,··· 请问,第2025 个质数是多少?

【答案提交】 这是一道结果填空题,你只需要算出结果后提交即可。本题的结果为一个 整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

思路

​ 筛质数,从小到大就行了,一般来说小于等于 n 的质数个数为 n/ln(n) , (acwing上听的,查了一下是素数定理)最后的数不会太大

代码

#include <bits/stdc++.h> 
using namespace std;
#define int  long long
int primes[2030];


signed main()
{
	int cnt = 0;
	for(int i = 2; ; i ++)
	{
		bool flag = true;
		for(int j = 0; j < cnt; j ++)
			if(i % primes[j] == 0)
			{
				flag = false;
				break;
			}
		if(flag)primes[cnt ++] = i;
		if(cnt == 2025)break;
	}
	cout<<primes[2024]<<endl; //17609
	return 0;
} 

B: 黑白棋

本题总分:5分

【问题描述】 小蓝最近迷上了一款名为“黑白棋填充”的游戏。该游戏在一个方形网格棋 盘上进行,其中部分格子已经填有黑色或白色的棋子,而其他格子为空,等待 玩家填入棋子。 游戏规则是,玩家需要按照以下规则填满整个棋盘,才能算作胜利:

  1. 黑白棋子数量均等: 在每一行和每一列中,黑色棋子和白色棋子的数量必须相等。
  2. 相邻棋子限制: 在棋盘的任何一行或一列中,不能有超过两个相同颜色的棋子连续排列 (即不允许出现“黑黑黑”’或“白白白”的情况)。
  3. 行列唯一性: 每一行的棋子排列方式必须是唯一的,不能与棋盘中的任何其他行完全相 同。 每一列的棋子排列方式必须是唯一的,不能与棋盘中的任何其他列完全相 同。 行与列之间的棋子排列不作比较,即行可以与列相同,无需满足行列间的 唯一性。

image-20250413093817555

image-20250413093841280

思路

已经填好了一些棋子,然后要让最后的符合规则,本来用的dfs 一行完成check一下,一列完成check一下,然后跑了好长时间没跑出来,耗了半小时没耗出来然后去做后面了,后面该做的做完又回来看了一下,干脆直接枚举完再check一下,不容易出错,然后就出了。。。

前两道题还是得写得越简单越清晰越好

代码

#include <bits/stdc++.h> 
using namespace std;
const int N = 10;
int g[N][N];
#define int long long


bool arr(vector<int> &v)
{
	
	for(int i = 0; i + 2 < 6; i ++)
		if(v[i] == v[i + 1] && v[i + 1] == v[i + 2])return false;
		
	int cnt = 0;
	for(int i = 0; i < 6; i ++)
		if(v[i] == 1)cnt ++;
	if(cnt != 3)return false;
	
	return true;
}

bool check(int state)
{
	memset(g, 0, sizeof g);
	g[1][1] = g[5][3] = g[5][6] = g[6][5] = 1;
	g[1][2] = g[1][4] = g[2][4] = g[3][5] = g[3][6] = g[6][2] = 2;
	int cnt = 0;
	for(int i = 1; i <= 6; i ++)
		for(int j = 1; j <= 6; j ++)
			if(!g[i][j])
			{
				g[i][j] = (state >> cnt & 1) ? 1 : 2;
				cnt ++; 
			}
			
			
	map<vector<int>, bool> r, c;
	
	for(int i = 1; i <= 6; i ++) //r
	{
		vector<int> v;
		for(int j = 1; j <= 6; j ++)
			v.push_back(g[i][j]);
		
		if(!arr(v))return false;
		if(r[v])return false;
		r[v] = true;
	}
	
		
	for(int j = 1; j <= 6; j ++)
	{
		vector<int> v;
		for(int i = 1; i <= 6; i ++)
			v.push_back(g[i][j]);

		if(!arr(v))return false;
		if(c[v])return false;
		c[v] = true;
	}
	
	return true;
	
}

signed main()
{
	g[1][1] = g[5][3] = g[5][6] = g[6][5] = 1;
	g[1][2] = g[1][4] = g[2][4] = g[3][5] = g[3][6] = g[6][2] = 2;
	
	for(int i = 0; i < 1 << 26; i ++)
		if(check(i))break;
	
	for(int i = 1; i <= 6; i ++)
	{
		for(int j = 1; j <= 6; j ++)
		{
			cout<<abs(g[i][j] - 2);
		}
		cout<<endl;
	}
	return 0;
}

/*
101001
010011
101100
010110
011001
100110

101001010011101100010110011001100110
*/

C: 抽奖

时间限制: 1.0s 内存限制: 256.0MB 本题总分:10分

image-20250413095218956

image-20250413095522972

image-20250413095553394

image-20250413095607667

思路

​ 模拟题, 把编号 -1,每次转的时候加一下模一下n就行了

代码

#include <bits/stdc++.h> 
using namespace std;

const int N = 1010;

int a[N], b[N], c[N];

int check(int x, int y, int z)
{
	if(a[x] == b[y] && a[x] == c[z])return 200;
	else if(a[x] == b[y] || a[x] == c[z] || b[y] == c[z])return 100;
	else if(a[x] == b[y] - 1 && b[y] + 1 == c[z])return 200;
	else 
	{
		vector<int> v = {a[x], b[y], c[z]};
		sort(v.begin(), v.end());
		if(v[0] + 1 == v[1] && v[1] + 1 == v[2])return 100;

	}

	return 0;
}

int main()
{
	int n;cin>>n;
	for(int i = 0; i < n; i ++)cin>>a[i];
	for(int j = 0; j < n; j ++)cin>>b[j];
	for(int i = 0; i < n; i ++)cin>>c[i];
	int m;cin>>m;
	
	int id1 = 0, id2 = 0, id3 = 0;
	long long ans = 0;

	while(m --)
	{
		int a1, b1, c1;
		cin>>a1>>b1>>c1;

		id1 = (id1 + a1) % n, id2 = (id2 + b1) % n, id3 = (id3 + c1) % n;
		ans += check(id1, id2, id3);
	}
	cout<<ans<<endl;
	
	return 0;
}

/* 
4
3 2 4 1
2 2 2 2
4 3 0 9
3
4 4 4
3 1 1
40 39 2
*/ 

D: 红黑树

时间限制: 1.0s 内存限制: 256.0MB 本题总分:10分

image-20250413095914038

image-20250413100022227

image-20250413100039186

image-20250413100111838

思路

看题目是没学过的数据结构,不做了

一个根节点左边变右边不变,那么我们只要知道所求点的编号判断经过了几次右子树就行了,奇数次就黑,偶数次就红

编号等于当前层第几个 + 上边所有层 = k + 2^(n - 1) - 1

代码

#include <bits/stdc++.h> 
using namespace std;
#define int long long

bool check(vector<int> v)
{
	bool st = true;
	for(int i = v.size() - 2; i >= 0; i --)
		if(v[i]) //右树 
			st = !st;
	return st; 
}

signed main()
{
	int m;cin>>m;
	while(m --)
	{
		int n, k;
		vector<int> v;
		cin>>n>>k;
		k += (1LL << (n - 1)) - 1;
		while(k)
		{
			v.push_back(k&1);
			k/=2;
		}
		if(check(v))cout<<"RED"<<endl;
		else cout<<"BLACK"<<endl;
	}
}

E: 黑客

时间限制: 1.0s 内存限制: 256.0MB 本题总分:15分

image-20250413100739211

image-20250413100856119

image-20250413100913808

思路

先找矩阵的两维长度,存下可能的(n, m),然后看元素里有没有,注意这里要分n=m和n!=m

然后思考对于一个矩阵所有元素排列应该是怎么样的,只要是一个位置不一样就是一种不同的结果,那么n行m列元素完全可以排成一排,求所有元素全排列有多少种不同结果。

然后就是高中知识,对于一个相同的元素(k个)的排列重复结果是k!, 要求的结果就是(nm)! / (ki)!

对于阶乘,由于数据过大,要用到逆元知识,i的逆元= qmi(i, mod - 2); 逆元用快速幂求即可
PS:去洛谷测了一下,只过了40%,然后发现数组开成1e5了wuwuwu~,增大之后发现还是有一个点TLE了
代码

#include <bits/stdc++.h> 
using namespace std;
#define int long long
const int mod = 1000000007;

const int N = 500010;
int fact[N], infact[N];


int qmi(int a, int b)
{
	int res = 1;
	while(b)
	{
		if(b & 1)res = res * a % mod;
		a = a * a % mod;
		b >>= 1;
	}
	return res;
}

int cul(int n, map<int, int> &mp)
{
	vector<int> v;
	for(auto i: mp)
		if(i.second)v.push_back(i.second);
	int res = fact[n];
	for(auto i : v)res = res * infact[i] % mod;
	return res;
}

signed main()
{
	int n;cin>>n;
	map<int, int> mp; //记录元素 
	
	for(int i = 1; i <= n; i ++)
	{
		int x;cin>>x;
		mp[x] ++;
	}
	vector<pair<int, int>>v;
	n -= 2;
	for(int i = 1; i <= n / i; i ++) //统计n,m 的可能值 
		if(n % i == 0)v.push_back({i, n / i});
	int ans = 0;
	
	
	fact[0] = infact[0] = 1; 
	for(int i = 1; i < N; i ++)  // 存下阶乘与逆元 
	{
		fact[i] = fact[i - 1] * i % mod;
		infact[i] = infact[i - 1] * qmi(i, mod - 2) % mod;
	}
	

	for(auto i : v)
	{
		int a = i.first, b = i.second;
		map<int, int> tmp = mp;
		
		if(a == b) //相等情况 
		{
			if(tmp[a] >= 2)
			{
				tmp[a] -= 2;
				ans = (ans + cul(n, tmp) ) % mod;
			}
		}
		else
		{
			if(tmp[a] && tmp[b])
			{
				tmp[a] --, tmp[b] --;
				ans = (ans + cul(n, tmp) * 2 )% mod; // 由于(n, m), (m, n)个数相同直接*2即可 
			}
		}
		
	}
	cout<<ans<<endl;
	
	return 0;
}
/*
6
2 2 1 4 3 3
*/

F: 好串的数目

时间限制: 1.0s 内存限制: 256.0MB 本题总分:15分

image-20250413102726987

image-20250413102826519

image-20250413102854433

image-20250413102917409

思路

当时没细想,想着做后面的题,直接暴力5分钟解决了,然后后面两题不会写,就老实回来把这道题优化了

贴一下暴力时期珍贵代码照片

image-20250413103619854

image-20250413103549675

image-20250413103330843

优化思路就是找到连续的片段,记录下这些片段长度,然后片段内好串个数就是1-长度的等差数列求和,片段外好串只有两个相邻片段会有,个数为相邻片段长度相乘

代码

#include <bits/stdc++.h> 
using namespace std;
#define int long long

signed main()
{
	string s;cin>>s;
	int n = s.size();
	vector<int> v;
	v.push_back(0);
	for(int i = 1; i < n; i ++)
		if(!(s[i] == s[i - 1] || s[i] == s[i - 1] + 1))v.push_back(i);
	
	v.push_back(n);
	
	vector<int> v2;
	for(int i = 1; i < v.size(); i ++)
		v2.push_back(v[i] - v[i - 1]);

	
	int num[100010];
	for(int i = 1; i <= n; i ++)num[i] = i + num[i - 1];
	int ans =0;
	for(int i = 0; i < v2.size(); i ++)
	{
		ans += num[v2[i]];
		if(i)ans += v2[i] * v2[i - 1];
	}
	cout<<ans;
	return 0;
}

最后两题就真一点不会,然后去优化前面的题去了,慢慢悠悠到后面发现没时间了,幸好手速过快,并且过于大胆,最后的时候优化完过了一个样例就交了,另一个样例还是结束完测的要是没过就紫砂了

不过想吐槽一下这代码界面没法ctrlA全选删除,还得自己用鼠标划着删,还有pdf复制样例没法直接粘到控制台,会漏空格

image-20250413110146350

屏幕截图 2025-04-13 105340

posted @ 2025-04-13 11:11  与君弈  阅读(207)  评论(0)    收藏  举报