2025年蓝桥杯省赛A整理
1.寻找质数
本题较为简单,我们直接枚举得到第2025个质数即可.
cpp
//直接使用质数判定的模板即可
#include<iostream>
#include<cstdio>
using namespace std;
bool check(int x){
for (int i=2;i*i<=x;i++){
if (x%i==0)
return false;
}
return true;
}
int idx=1;
int main(){
for (int i=3;i<=10000000;i++){
if (check(i))
idx++;
if (idx==2025){
cout<<i<<endl;
return 0;
}
}
return 0;
}
2. 黑白棋
该题目可以通过逻辑推理零代码解决。且求解过程更加简洁。
下面我们考虑使用搜索的方法解决该题。
考虑到最终棋盘上黑棋的个数是确定的,那么我们可以将所有待填的位置打表记录。然后依次dfs搜索,对于每个完成搜索的棋盘,我们建立一个check()函数检查其是否符合要求。
cpp
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
using namespace std;
bool vs[10][10];
bool vt[10][10];
int g[10][10];
int choose[25][2]={{2,6},{2,7},{3,2},{3,3},{3,4},{3,6},{3,7},{4,2},{4,3},{4,4},{5,2},{5,3},{5,4},{5,5},{5,6},{5,7},{6,2},{6,3},{6,5},{6,6},{7,2},{7,4},{7,5},{7,7}};
int ans=0;
bool check(){
for (int i=2;i<=5;i++){
for (int j=2;j<=7;j++){
if (g[i][j]==g[i+1][j]&&g[i+1][j]==g[i+2][j])
return false;
}
}
for (int i=2;i<=7;i++){
for (int j=2;j<=5;j++)
if (g[i][j]==g[i][j+1]&&g[i][j+1]==g[i][j+2])
return false;
}
for (int i=2;i<=7;i++){
for (int j=i+1;j<=7;j++){
for (int k=2;k<=7;k++){
if (g[i][k]!=g[j][k])
break;
else if (k==7&&g[i][k]==g[j][k])
return false;
}
}
}
for (int i=2;i<=7;i++){
for (int j=i+1;j<=7;j++){
for (int k=2;k<=7;k++){
if (g[k][i]!=g[k][j])
break;
else if (k==7&&g[k][i]==g[k][j])
return false;
}
}
}
for (int i=2;i<=7;i++){
int sumi=0;
int sumj=0;
for (int j=2;j<=7;j++){
sumi+=g[i][j];
sumj+=g[j][i];
}
if (sumi!=3)
return false;
if (sumj!=3)
return false;
}
return true;
}
void dfs(int idx,int place){ // 将第 idx个0放到g[place]处
if (idx>=13)
return;
if (idx <12&&place >=25){
return ;
}
g[choose[place][0]][choose[place][1]]=0;
if (idx>=12){
if (check()){
ans++;
for (int i=2;i<=7;i++){
for (int j=2;j<=7;j++)
cout<<g[i][j]<<' ';
cout<<endl;
}
cout<<endl<<endl<<endl;
cout<<"答案是:";
for (int i=2;i<=7;i++){
for (int j=2;j<=7;j++){
cout<<g[i][j];
}
}
cout<<endl;
}
}
for (int j=place +1;j<=24;j++){
dfs(idx+1,j);
g[choose[j][0]][choose[j][1]]=1;
}
}
int main(){
for (int i=2;i<=7;i++){
for (int j=2;j<=7;j++){
g[i][j]=1;
}
cout<<endl;
}
memset(vs,false,sizeof vs);
g[2][2]=1,g[2][3]=0,g[2][4]=1,g[2][5]=0;
vs[2][2]=vs[2][3]=vs[2][4]=vs[2][5]=true;
vt[2][2]=vt[2][3]=vt[2][4]=vt[2][5]=true;
g[3][5]=0;
vs[3][5]=true;
vt[3][5]=true;
g[4][5]=1,g[4][6]=0,g[4][7]=0;
vs[4][5]=vs[4][6]=vs[4][7]=true;
vt[4][5]=vt[4][6]=vt[4][7]=true;
g[6][4]=1,g[6][7]=1;
vs[6][4]=vs[6][7]=true;
vt[6][4]=vt[6][7]=true;
g[7][3]=0,g[7][6]=1;
vs[7][3]=vs[7][6]=true;
vt[7][3]=vt[7][6]=true;
for (int i=2;i<=7;i++){
for (int j=2;j<=7;j++){
cout<<g[i][j]<<' ';
}
cout<<endl;
cout<<endl;
}
for (int i=0;i<=24;i++){
dfs(1,i);
}
cout<<"数目为"<<ans<<endl;
return 0;
}
3.抽奖
本题为直接模拟题,我们直接上代码。
cpp
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
int n;
const int N=1e3+10;
int a[N];
int b[N];
int c[N];
int m;
int ans=0;
int x=0;int y=0;int z=0;
int cal(int x1,int x2,int x3){
if (x1==x2&&x2==x3)
return 200;
if (x1+1==x2&&x2+1==x3)
return 200;
if (x1==x2||x1==x3||x2==x3)
return 100;
int t1=min(min(x1,x2),x3);
int t3=max(max(x1,x2),x3);
int t2=x1+x2+x3-t1-t3;
if (t1+1==t2&&t2+1==t3)
return 100;
return 0;
}
int main(){
cin>>n;
for (int i=0;i<n;i++){
scanf("%d",&a[i]);
}
for (int i=0;i<n;i++){
scanf("%d",&b[i]);
}
for (int i=0;i<n;i++){
scanf("%d",&c[i]);
}
cin>>m;
for (int i=1;i<=m;i++){
int dx,dy,dz;
scanf("%d%d%d",&dx,&dy,&dz);
x=(x+dx)%n;
y=(y+dy)%n;
z=(z+dz)%n;
ans+=cal(a[x],b[y],c[z]);
}
cout<<ans<<endl;
return 0;
}
4.红黑树
本题也较为简单,找到规律后直接递归模拟即可。
cpp
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
int n;
int dfs(int a,int b){
if (a==1&&b==1)
return 1;
else {
if (b&1)
return dfs(a-1,(b+1)>>1);
else
return -dfs(a-1,(b+1)>>1);
}
}
int main(){
cin>>n;
for (int i=1;i<=n;i++){
int m,k;
scanf("%d%d",&m,&k);
if (dfs(m,k)==1)
cout<<"RED"<<endl;
else
cout<<"BLACK"<<endl;
}
return 0;
}
5.黑客
(黑客)[https://www.lanqiao.cn/problems/20531/learning/\]
本题事实上是一个计数问题,我们这样来考虑最后结果的计算。首先我们需要考虑矩阵的行数和列数的可能只。考虑到输入的数input= n × m + 2 n\times m+2 n×m+2,因此,我们只需要对input-2的所有因数进行枚举,即可求出矩阵行数列数的所有可能值。之后我们接收输入的 n \\times m+2 个数,考虑到a_i的取值范围较小,所以我们直接用桶来存储每个a[i]的个数。之后我们枚举input-2的所有因数(期望值个数:ln input),之后我们从1到N遍历桶中的所有数,考虑若当前已经完成了k个位置的排序,那么若a[i]的个数为m个,那么此时将这m个数放置在矩阵中的方案数即为 C i n p u t − 2 − k m C_{input-2-k}^{m} Cinput−2−km.按照这样的方法进行相乘、求和,我们即可完成这道题的求解。复杂度为(O(nflnn)),其中f为运算 C i n p u t − 2 − k m C_{input-2-k}^{m} Cinput−2−km的方案数。
我们考虑 C i n p u t − 2 − k m = ( i n p u t − 2 − k ) ! m ! ( i n p u t − 2 − k − m ) ! C_{input-2-k}^{m}=\frac{(input-2-k)!}{m!(input-2-k-m)!} Cinput−2−km=m!(input−2−k−m)!(input−2−k)!
一个自然的想法是提前预处理 i ! , ( i ! ) − 1 i!,(i!)^{-1} i!,(i!)−1,这样上面主程序的复杂度就变成了O(nlnn)
我们再考虑预处理的复杂度,我们可以直接利用阶乘关系O(n)计算i!,对于i!的逆元,我们考虑快速幂算法,那么每个i计算的复杂度就是O(lnp-2),也就是说处理逆元的复杂度就是O(nlnp).该复杂度属于我们可以接受的范围。
在这里我们复习一下快速幂算法以及求逆元的几种方法.
快速幂
cpp
//快速幂
int qpow(int a,int b){
int ans=1;
while (b){
if (b&1) ans*=a;
a*=a;
b>>=1;
}
return ans;
}
求逆元
cpp
//快速幂求逆元
//O(log m)
long long M=1e9+7;
long long q_pow(long long x,int y){//calculate x^y
long long ans=1;
while (y){
if (y&1){
ans=ans*x%M;
}
x=x*x%M;
y>>=1;
}
return ans;
}
cout<<qpow(x,M-2)<<endl;
cpp
//exgcd求逆元
//O(log min(a,m))
void exgcd(int a,int b,int &x,int &y){
if (!b){
x=1;
y=0;
}
else{
exgcd(b,a%b,y,x);
y-=a/b*x;
}
}
int inverse(int a,int m){
int x,y;
exgcd(a,m,x,y);
return (x%m+m)%m;
}
cpp
//线性求逆元
//O(n+logm)处理1~n模m的逆元
long long inv[N];
void getinv(long long m){
inv[1]=1;
for (int i=2;i<mod;i++){
inv[i]=(m-m/i)*inv[mod%i]%mod;
}
}
在这种和取余相关的问题中,我们需要格外注意整数溢出问题,我们在这里整理一下int 和long long 的大致范围。
int :2e9,long long :9e18
在本题中我们可以使用快速幂或者exgcd两种方法来预处理逆元。
cpp
//快速幂解法
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
using namespace std;
const int N=5e5+10;
int input;
int a[N][2];
int idx=1;
int nums[N];
long long fact[N];
const int M=1e9+7;
long long inv[N];
int res=0;
void factor(int x){
for (int i=1;i<=x;i++){
if (x%i==0){
a[idx][0]=i;
a[idx][1]=x/i;
idx++;
}
}
}
int get_fact(){
long long x=input-2;
long long ans=1;
for (int i=1;i<=5e5+1;i++){
if (nums[i]<0)
return 0;
if (nums[i]>0){
ans=(ans*fact[x])%M;
ans=(ans*inv[nums[i]])%M;
ans=(ans*inv[x-nums[i]])%M;
x-=nums[i];
}
if (x==0)
return ans;
}
return ans;
}
long long q_power(long long x,int y){//calculate x^y
long long ans=1;
while (y){
if (y&1){
ans=ans*x%M;
}
x=x*x%M;
y>>=1;
}
return ans;
}
int main(){
cin>>input;
factor(input-2);
fact[0]=1;
inv[0]=1;
for (int i=1;i<=5e5+5;i++){
fact[i]=fact[i-1]*i%M;
}
for (int i=1;i<=input;i++){
int in;
scanf("%d",&in);
nums[in]++;
}
for (int i=1;i<=5e5+5;i++){
inv[i]=q_power(fact[i],M-2);
}
for (int i=1;i<idx;i++){
if (nums[a[i][0]]<1||nums[a[i][1]]<1)
continue;
nums[a[i][0]]--;
nums[a[i][1]]--;
res=(res+get_fact())%M;
nums[a[i][0]]++;
nums[a[i][1]]++;
}
cout<<res<<endl;
return 0;
}
cpp
//exgcd解法
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
using namespace std;
const int N=5e5+10;
int input;
int a[N][2];
int idx=1;
int nums[N];
long long fact[N];
const int M=1e9+7;
long long inv[N];
int res=0;
void factor(int x){
for (int i=1;i<=x;i++){
if (x%i==0){
a[idx][0]=i;
a[idx][1]=x/i;
idx++;
}
}
}
int get_fact(){
long long x=input-2;
long long ans=1;
for (int i=1;i<=5e5+1;i++){
if (nums[i]<0)
return 0;
if (nums[i]>0){
ans=(ans*fact[x])%M;
ans=(ans*inv[nums[i]])%M;
ans=(ans*inv[x-nums[i]])%M;
x-=nums[i];
}
if (x==0)
return ans;
}
return ans;
}
long long q_power(long long x,int y){//calculate x^y
long long ans=1;
while (y){
if (y&1){
ans=ans*x%M;
}
x=x*x%M;
y>>=1;
}
return ans;
}
void exgcd(long long a,long long b,long long &x,long long &y){
if (!b){
x=1;
y=0;
}
else{
exgcd(b,a%b,y,x);
y-=a/b*x;
}
}
long long inverse(long long a,long long m){
long long x,y;
exgcd(a,m,x,y);
return ((x%m)+m)%m;
}
int main(){
cin>>input;
factor(input-2);
fact[0]=1;
inv[0]=1;
for (int i=1;i<=5e5+5;i++){
fact[i]=fact[i-1]*i%M;
}
for (int i=1;i<=input;i++){
int in;
scanf("%d",&in);
nums[in]++;
}
for (int i=1;i<=5e5+5;i++){
inv[i]=inverse(fact[i],M);
}
for (int i=1;i<idx;i++){
if (nums[a[i][0]]<1||nums[a[i][1]]<1)
continue;
nums[a[i][0]]--;
nums[a[i][1]]--;
res=(res+get_fact())%M;
nums[a[i][0]]++;
nums[a[i][1]]++;
}
cout<<res<<endl;
return 0;
}
6.好串的数目
(好串的数目)[https://www.lanqiao.cn/problems/20524/learning/\]
本题的思路为换维思考,如果我们分类计算长度为1,长度为2...长度为k的好串,计数过程将难以进行。我们 换维思考,考虑数列中的每个数对于最终结果中好串的数目的贡献。
对于每个数,我们考虑以它作为起点的好串的个数如何统计。
如果该数的下一个数的值与该数相等,或者值为该数的值加1.那么自然的,将这个数并入当前串中,仍然是一个好串。
因此一个自然的想法是:我们可以对输入的数列进行分划,将其划分为若干了连续非递减子串的并。
并用sl和sr两个数组分别记录每个非递减子串的左右端点。
则对于数列中的每个点,其作为起点时,对最终结果的贡献可以如下计算:
它在它所在的连续非递减子串中距离又端点的数的个数+下一个非连续递减子串的长度。
cpp
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
using namespace std;
const int N=1e5+7;
char s[N];
int l=0;
int r=0;
int sl[N];
int sr[N];
int idx=1;
int cnt=1;
long long ans=0;
int main(){
cin>>s;
while (r<strlen(s)){
if (s[r+1]==s[r]||s[r+1]==s[r]+1)
r++;
else{
sl[idx]=l;
sr[idx]=r;
l=r+1;
r=l;
idx++;
}
}
int i=0;
while (i<strlen(s)){
if (i>=sl[cnt]&&i<=sr[cnt]){
ans+=sr[cnt]-i+1+sr[cnt+1]-sl[cnt+1]+1;
i++;
}
else {
cnt++;
}
}
cout<<ans-(sr[idx-1]-sl[idx-1]+1)<<endl;
//这是因为最后一段的每个点都多算了1,因此需要减去最后一段长度个数个1,因此就是要减去最后一段的长度。
return 0;
}
7.地雷阵
(地雷阵)[https://www.lanqiao.cn/problems/20515/learning/\]
本题虽然题号较为靠后,但题目本身并不困难。我们只需找到相切时的临界状态,然后求出对于每一个圆的可行区间,之后我们进行区间合并即可。
cpp
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<cmath>
using namespace std;
int n;
const int N=100100;
double x[N];
double y[N];
double r[N];
struct node{
double left;
double right;
friend bool operator <(node a,node b){
if (a.left!=b.left)
return a.left<b.left;
return a.right<b.right;
}
};
node a[N];
double L,R;
double add=0;
double ans=0;
int main(){
cin>>n;
for (int i=1;i<=n;i++){
scanf("%lf%lf%lf",&x[i],&y[i],&r[i]);
double xie=atan(y[i]/x[i]);
double delta=asin(r[i]/sqrt(x[i]*x[i]+y[i]*y[i]));
a[i].left=xie-delta;
a[i].right=xie+delta;
}
sort(a+1,a+n+1);
for (int i=1;i<=n;i++){
if (a[i].left>R){
add+=R-L;
L=a[i].left;
R=a[i].right;
continue;
}
R=max(R,a[i].right);
}
add+=R-L;
ans=1-2*add/3.1415926535897932;
printf("%.3f",ans);
return 0;
}
8.扫地机器人
本题作为压轴题,在笔者看来较为困难。笔者现阶段还无法AC该道题,但笔者通过直接进行暴力搜索,成功通过了20%的测试点。之后笔者又再此基础上,加上了对一些特殊情况的特殊判定。基于若图为欧拉图,一定可以取遍所有的点,而且一个图为欧拉图等价于这个图每个顶点的度都是偶数。而若有2个奇数点,则图中一定存在欧拉路,与欧拉闭迹一样,我们同样可以访问所有的点。若奇数点的个数为1,则由于图中只有n条边,故我们可知图的结构为一个圈和一条路并。该图中的所有点一定也可以被访问。
根据上面的思路,我们可以在进行dfs之前,对奇点的个数进行统计,若奇点的个数小于等于2,那么我们的答案就为所有点中待清理的点的个数。若奇点的个数大于2,我们再进行dfs()求解,通过该优化,我们成功又通过了10%的测试点。
最终成功通过了30%的测试点。
cpp
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
using namespace std;
const int N=2e6+7;
int n;
int a[N];
int h[N],e[N],ne[N],idx;
int pr[N];
void init(){
memset(h,-1,sizeof h);
idx=0;
}
int add(int a,int b){
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
return idx-1;
}
int vtp[N];
bool vte[N];
int res=0;
int ans=0;
void dfs(int x,int edge){//表示现在遍历到了点x,现在已经记录到的需要清理的节点数为ans
if (a[x]==1&&(!vtp[x])){//如果这个点需要被清扫
vtp[x]++;
ans++;
res=max(res,ans);
}
for (int i=h[x];~i;i=ne[i]){
int j=e[i];
if (vte[i]==true)
continue;
vte[i]=true;
vte[pr[i]]=true;
//cout<<x<<" "<<j<<" "<<ans<<endl;
dfs(j,i);
}
vte[edge]=false;
vte[pr[edge]]=false;
vtp[x]--;
if (a[x]==1&&(!vtp[x]))
ans--;
}
int main(){
init();
cin>>n;
for (int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
int x,y;
for (int i=1;i<=n;i++){
scanf("%d%d",&x,&y);
int a=add(x,y);
int b=add(y,x);
pr[b]=a;
pr[a]=b;
}
for (int i=1;i<=n;i++){
memset(vte,0,sizeof vte);
memset(vtp,0,sizeof vtp);
ans=0;
dfs(i,0);
}
cout<<res<<endl;
return 0;
}