首页 > 编程语言 >2022年第十三届蓝桥杯大赛软件类省赛C/C++大学A组真题

2022年第十三届蓝桥杯大赛软件类省赛C/C++大学A组真题

时间:2023-03-05 21:56:54浏览次数:65  
标签:组真题 const 类省赛 CI 蓝桥 int include RI define

Preface

周末没什么比赛打索性开始准备下蓝桥杯,然后就想着找一下去年的真题来做一下

结果yysy去年的真题说实话有点难度的,感觉出题风格偏向OI比赛而和ACM的风格不太像啊

感觉考察的都比较偏技巧性,而思维难度说实话不是很大

反映在题目上就是虽然有DP和数论之类的,但都可以一眼秒

但涉及到数据结构之类的就比较多套路了,不得不说现在写个线段树上二分都能调一早上的我真是屑

以下填空题的题目可以看这里,后面的代码题可以在这里提交


A 裁纸刀

SB题,直接先切\(4\)刀,然后切\(19\)刀把行分开来,最后切\(20\times 21\)刀把每个二维码独立分出来

手算一下答案就是\(443\),这做不出来还是当场remake吧


B 灭鼠先锋

博弈论裸题了,数据范围很小直接爆搜即可

一句话:必败态的所有后继状态都是必胜态,必胜态的后继状态只要有一个必败态即可

简单写个代码跑一下答案是LLLV

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
int a[3][5]; bool ans[5];
inline bool DFS(CI tot)
{
	if (tot==8) return 1; bool flag=0;
	for (RI i=1,j;i<=2;++i) for (j=1;j<=4;++j) if (!a[i][j])
	{
		if (a[i][j]=1,flag|=!DFS(tot+1),j<4&&!a[i][j+1])
		a[i][j+1]=1,flag|=!DFS(tot+2),a[i][j+1]=0; a[i][j]=0;
	}
	return flag;
}
int main()
{
	a[1][1]=1; ans[1]=!DFS(1);
	a[1][2]=1; ans[2]=!DFS(2);
	a[1][1]=0; ans[3]=!DFS(1);
	a[1][3]=1; ans[4]=!DFS(2);
	for (RI i=1;i<=4;++i) putchar(ans[i]?'V':'L');
	return 0;
}

C 求和

不妨设\(sum=\sum_{i=1}^ n a_i\),然后枚举每个数,先把\(sum\)减去\(a_i\),然后用\(a_i\)乘上\(sum\)即可

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=200005;
int n,a[N]; long long sum,ans;
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	RI i; for (scanf("%d",&n),i=1;i<=n;++i) scanf("%d",&a[i]),sum+=a[i];
	for (i=1;i<=n;++i) sum-=a[i],ans+=1LL*a[i]*sum;
	return printf("%lld",ans),0;
}

D 选数异或

考虑对于每个数\(a_i\)求出在它之前且距离它最近的\(j\),满足\(a_i\oplus a_j=x\),这个可以通过一个桶轻松实现,我们记\(pre_i=j\)

然后我们考虑处理询问\([l,r]\),不难发现只要\(\exist i\in[l,r],s.t. pre_i\ge l\)即可找到这样一对数

ST表维护下区间最大值即可,每次判断下最大值是否大于\(l\)即可

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=200005;
int n,m,l,r,a[N],x,bkt[N],mx[N][25],lg[N];
inline int query(CI l,CI r)
{
	int k=lg[r-l+1]; return max(mx[l][k],mx[r-(1<<k)+1][k]);
}
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	RI i,j; for (scanf("%d%d%d",&n,&m,&x),i=1;i<=n;++i)
	scanf("%d",&a[i]),mx[i][0]=bkt[a[i]^x],bkt[a[i]]=i;
	for (i=2;i<=n;++i) lg[i]=lg[i>>1]+1;
	for (j=1;(1<<j)<=n;++j) for (i=1;i+(1<<j)-1<=n;++i)
	mx[i][j]=max(mx[i][j-1],mx[i+(1<<j-1)][j-1]);
	for (i=1;i<=m;++i) scanf("%d%d",&l,&r),puts(query(l,r)>=l?"yes":"no");
	return 0;
}

E 爬树的甲壳虫

刚开始转移方程写反了,WA了好几遍才反应过来期望要倒着推

我们设\(f_i\)表示从高度\(i\)到顶部花费的期望时间,不难列出以下转移方程:

\[f_{n}=0\\ f_{i}=(1-p_{i+1})\times f_{i-1}+p_{i+1}\times f_0+1\ \ \ (i\in[1,n-1]) \]

然后乍一看这个状态成环了不好处理,但我们可以用这道题的套路,把\(f_0\)当作已知量,把每个变量的值表示成\(x\times f_0+y\)的形式,最后反推出\(f_0\)的值即可

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=100005,mod=998244353;
inline int sum(CI x,CI y)
{
	return x+y>=mod?x+y-mod:x+y;
}
inline int sub(CI x,CI y)
{
	return x-y<0?x-y+mod:x-y;
}
inline int quick_pow(int x,int p=mod-2,int mul=1)
{
	for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul;
}
struct Data // x*f[0]+y
{
	int x,y;
	inline Data(CI X=0,CI Y=0)
	{
		x=X; y=Y;
	}
	friend inline Data operator + (const Data& A,const Data& B)
	{
		return Data(sum(A.x,B.x),sum(A.y,B.y));
	}
	friend inline Data operator - (const Data& A,const Data& B)
	{
		return Data(sub(A.x,B.x),sub(A.y,B.y));
	}
	friend inline Data operator * (const Data& A,const int& mul)
	{
		return Data(1LL*A.x*mul%mod,1LL*A.y*mul%mod);
	}
}f[N]; int n,x,y,p[N];
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	RI i; for (scanf("%d",&n),i=1;i<=n;++i) scanf("%d%d",&x,&y),p[i]=1LL*x*quick_pow(y)%mod;
	for (f[n]=Data(0,0),i=n-1;i>=0;--i) f[i]=(f[i+1]*sub(1,p[i+1]))+Data(p[i+1],1);
	return printf("%d",1LL*f[0].y*quick_pow(sub(1,f[0].x))%mod),0;
}

F 青蛙过河

首先一眼二分答案\(k\),同时不难发现来回跳\(x\)次等价于从家跳到学校\(2x\)次

然后对着样例大力猜测只要连续的\(k\)个石头的和大于等于\(2x\)即可,交一发发现过了

证明的话稍微想一下也很容易,我们先考虑所有连续的\(k\)个石头的和都等于\(2x\)的情况

此时由于所有区间和都相等,因此必然有\(h_i=h_{i+k}\),因此直接从家里跳到第一个区间的某个\(i\)上,然后一直往后跳\(k\)即可

然后如果存在某个区间的和大于\(2x\)的情况,我们必然可以通过减去掉某些石头的高度,来使得最后所有区间的和都是\(2x\)

因此check就很简单了

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=100005;
int n,a[N],x,ans;
inline int check(CI k)
{
	long long sum=0; RI i; for (i=1;i<=k;++i) sum+=a[i];
	if (sum<2LL*x) return 0;
	for (i=k+1;i<n;++i) if ((sum+=a[i]-a[i-k])<2LL*x) return 0;
	return 1;
}
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	RI i; for (scanf("%d%d",&n,&x),i=1;i<n;++i) scanf("%d",&a[i]);
	int l=1,r=n-1,mid; ans=n; while (l<=r)
	if (check(mid=l+r>>1)) ans=mid,r=mid-1; else l=mid+1;
	return printf("%d",ans),0;
}

G 最长不下降子序列

这个算是比较套路的题了,放在以前可能看一眼就秒了,现在还要想上好久

不难发现我们在修改连续的\(k\)个数字变成同一值时,最好让这\(k\)个数和它们前面的数一样

我们先维护\(f_i\)表示以\(i\)结尾的最长不下降子序列的长度,考虑枚举\(i\)使得\([i+1,i+k]\)都变成\(a_i\)

不难发现此时的答案就是\(f_i+k\)再加上在\([i+k+1,n]\)中以\(a_i\)开头的最长不下降子序列的长度

当然我们也可以不考虑用\(a_i\)开头,舍弃掉前面的贡献只考虑后面的最优贡献,这个特判一下即可

而维护前缀和后缀的最长不下降子序列都可以通过对值域开树状数组来维护,总复杂度\(O(n\log n)\)

#include<cstdio>
#include<iostream>
#include<algorithm>
#define RI register int
#define CI const int&
using namespace std;
const int N=100005;
int n,k,a[N],rst[N],f[N],tot,ans;
#define lowbit(x) (x&-x)
class Tree_Array1 //<=
{
	private:
		int bit[N];
	public:
		inline void add(RI x,CI y)
		{
			for (;x<=tot;x+=lowbit(x)) bit[x]=max(bit[x],y);
		}
		inline int get(RI x,int ret=0)
		{
			for (;x;x-=lowbit(x)) ret=max(ret,bit[x]); return ret;
		}
}T1;
class Tree_Array2 //>=
{
	private:
		int bit[N];
	public:
		inline void add(RI x,CI y)
		{
			for (;x;x-=lowbit(x)) bit[x]=max(bit[x],y);
		}
		inline int get(RI x,int ret=0)
		{
			for (;x<=tot;x+=lowbit(x)) ret=max(ret,bit[x]); return ret;
		}
}T2;
#undef lowbit
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	RI i; for (scanf("%d%d",&n,&k),i=1;i<=n;++i) scanf("%d",&a[i]),rst[i]=a[i];
	sort(rst+1,rst+n+1); tot=unique(rst+1,rst+n+1)-rst-1;
	for (i=1;i<=n;++i) a[i]=lower_bound(rst+1,rst+tot+1,a[i])-rst;
	for (i=1;i<=n;++i) f[i]=T1.get(a[i])+1,T1.add(a[i],f[i]);
	for (i=n;i>k;--i)
	{
		ans=max(ans,f[i-k]+k-1+T2.get(a[i-k])+1);
		int tmp=T2.get(a[i])+1;	ans=max(ans,k+tmp); T2.add(a[i],tmp);
	}
	return printf("%d",ans),0;
}

H 扫描游戏

思路很好想,写起来就是不太舒服,排序和统计的地方写起来都有点细节的说

首先一眼要把所有点按照顺时针遇到的顺序排序,由于我把计算几何忘得差不多了,因此就写了很粗暴的解析几何方法

我们考虑设原点与待排序点的连线与\(y\)轴正半轴顺时针旋转形成的角为\(\theta\),显然\(\tan \theta=\frac{x}{y}\)

但显然不能直接排序,因此我直接想到把每个点对应的象限搞出来,排序时候先看象限,如果象限相同此时\(\tan \theta\)单调,再比较\(\frac{x}{y}\)的值即可

然后我们考虑如何快速统计答案,我们考虑假设上一个被扫过的点位置为\(lst\)(初始为\(0\)),棒的长度为\(L\),我们要找的就是在\(lst\)顺时针方向之后的第一个\(x_i^2+y_i^2\le L^2\)的位置

我们用线段树维护区间的\(x_i^2+y_i^2\)的最小值,每次先查询\([lst+1,n]\)中是否有满足要求的点,没有就查询\([1,lst-1]\)

然后要注意判断找到的新点和上一个点是否是在同一直线上,这里还是有点坑的

最后最傻逼的一个地方来了,由于\(L\)不断累加会招致在后面计算\(L^2\)时爆long long,然后我又不愿意用double存真实值降低精度

不过最后转念一想,由于\(x_i^2+y_i^2\)的范围是\(\le 2\times 10^9\)的,因此给\(L\)定一个上界即可,多了也没有意义

最后折腾了一早上终于过了,可喜可贺

#include<cstdio>
#include<iostream>
#include<algorithm>
#define RI register int
#define CI const int&
using namespace std;
typedef long long LL;
const int N=200005;
const LL INF=(1ull<<63)-1,lim=2e9;
inline LL sqr(CI x)
{
	return 1LL*x*x;
}
struct point
{
	int x,y,z,d,id; LL dis;
	friend inline bool operator < (const point& A,const point& B)
	{
		if (A.d!=B.d) return A.d<B.d;
		if (1LL*A.x*B.y!=1LL*B.x*A.y) return 1LL*A.x*B.y<1LL*B.x*A.y;
		return A.dis<B.dis;
	}
}a[N]; int n,ans[N],lst,nxt,rk; LL L,SL;
class Segement_Tree
{
	private:
		LL mi[N<<2];
		inline void pushup(CI now)
		{
			mi[now]=min(mi[now<<1],mi[now<<1|1]);
		}
	public:
		#define TN CI now=1,CI l=1,CI r=n
		#define LS now<<1,l,mid
		#define RS now<<1|1,mid+1,r
		inline void build(TN)
		{
			if (l==r) return (void)(mi[now]=a[l].dis);
			int mid=l+r>>1;	build(LS); build(RS); pushup(now);
		}
		inline void remove(CI pos,TN)
		{
			if (l==r) return (void)(mi[now]=INF); int mid=l+r>>1; 
			if (pos<=mid) remove(pos,LS); else remove(pos,RS); pushup(now);
		}
		inline bool query(CI beg,CI end,TN)
		{
			if (beg>end) return 0; if (l==r) return nxt=l,mi[now]<=SL;	int mid=l+r>>1;
			if (beg<=mid)
			{
				if (mi[now<<1]<=SL&&query(beg,end,LS)) return 1;
			}
			if (end>mid)
			{
				if (mi[now<1|1]<=SL&&query(beg,end,RS)) return 1;
			}
			return 0;
		}
		#undef TN
		#undef LS
		#undef RS
}SEG;
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	RI i; for (scanf("%d%lld",&n,&L),i=1;i<=n;++i)
	{
		scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
		if (a[i].x>=0&&a[i].y>0) a[i].d=1; else
		if (a[i].x>0&&a[i].y<=0) a[i].d=2; else
		if (a[i].x<=0&&a[i].y<0) a[i].d=3; else
		a[i].d=4; a[i].id=i; ans[i]=-1; a[i].dis=sqr(a[i].x)+sqr(a[i].y);
	}
	for (sort(a+1,a+n+1),SEG.build(),SL=L*L;;)
	{
		if (++rk,!SEG.query(lst+1,n)&&!SEG.query(1,lst-1)) break;
		if (!lst) ans[a[nxt].id]=rk; else
		{
			if (a[nxt].d==a[lst].d&&1LL*a[nxt].x*a[lst].y==1LL*a[nxt].y*a[lst].x)
			ans[a[nxt].id]=ans[a[lst].id]; else ans[a[nxt].id]=rk;
		}
		lst=nxt; SEG.remove(nxt); L+=a[nxt].z; L=min(L,lim); SL=L*L;
	}
	for (i=1;i<=n;++i) printf("%d ",ans[i]);
	return 0;
}

I 数的拆分

妈的这题比前面的EFGH都简单多了,放那么后面比赛的时候都不一定能见到这道题

首先一眼先给\(n\)分解质因数,然后考虑每个质因数的指数\(c_1,c_2,\cdots,c_k\)

不难发现如果\(c_i=1\)的话显然无解,否则我们总可以用一下方法构造出形如\(x_1^2x_2^3\)的方案:

  • 若\(c_i\)为偶数,则直接让\(x_1=x_1\times p_i^{\frac{c_i}{2}}\)即可
  • 若\(c_i\)为奇数,此时有\(c_i\ge 3\),则让\(x_1=x_1\times p_i^{\frac{c_i-3}{2}},x_2=x_2\times p_i\)即可

因此我们只要枚举\((10^{18})^{\frac{1}{5}}\)范围的质数即可,最后再判断下多余下来的部分是不是平方数或者立方数即可

#include<cstdio>
#include<iostream>
#include<cmath>
#define RI register int
#define CI const int&
#define CL const long long&
using namespace std;
const int N=4005;
int t,pri[N],cnt; long long n; bool vis[N];
inline void init(CI n)
{
	for (RI i=2,j;i<=n;++i)
	{
		if (!vis[i]) pri[++cnt]=i;
		for (j=1;j<=cnt&&i*pri[j]<=n;++j)
		if (vis[i*pri[j]]=1,i%pri[j]==0) break;
	}
}
inline bool issqr(CL x)
{
	long long y=sqrt(x); return y*y==x||(y+1)*(y+1)==x;
}
inline bool iscube(CL x)
{
	long long y=pow(x,1.0/3.0); return y*y*y==x||(y+1)*(y+1)*(y+1)==x;
}
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t),init(4000);t;--t)
	{
		RI i; bool flag=1; scanf("%lld",&n);
		for (i=1;i<=cnt&&flag;++i) if (n%pri[i]==0)
		{
			if (n/=pri[i],n%pri[i]) flag=0; while (n%pri[i]==0) n/=pri[i];
		}
		if (!flag) puts("no"); else puts(n==1||issqr(n)||iscube(n)?"yes":"no");
	}
	return 0;
}

J 推导部分和

本来以为是个差分约束的题,不过这题保证有解了就直接搜索一下即可

对于\(l_i,r_i,s_i\)的信息,我们设\(pfx_i\)为原序列的前缀和数组,则有\(pfx_{r_i}-pfx_{l_i-1}=s_i\)

用差分约束的思想,我们可以建一条从\(l_i-1\to r_i\)权值为\(s_i\)的边,然后建一条\(r_i\to l_i-1\),权值为\(-s_i\)的边

然后询问的时候也同理,我们看下\(ql_i-1\)和\(qr_i\)是否在一个联通块内即可,不在的话显然就是UNKNOWN

最后求\(pfx\)的话直接DFS即可,随便指定一个点为\(0\)然后搜出联通块内其它关于它的相对值即可

#include<cstdio>
#include<iostream>
#include<vector>
#define RI register int
#define CI const int&
#define mp make_pair
#define pb push_back
#define fi first
#define se second
using namespace std;
typedef pair <int,long long> PI;
const int N=100005;
int n,m,q,l,r,fa[N]; long long s,pfx[N]; vector <PI> v[N]; bool vis[N];
inline int getfa(CI x)
{
	return fa[x]!=x?fa[x]=getfa(fa[x]):x;
}
inline void DFS(CI now)
{
	vis[now]=1; for (auto to:v[now])
	if (!vis[to.fi]) pfx[to.fi]=pfx[now]+to.se,DFS(to.fi);
}
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	RI i; for (scanf("%d%d%d",&n,&m,&q),i=1;i<=n;++i) fa[i]=i;
	for (i=1;i<=m;++i)
	{
		scanf("%d%d%lld",&l,&r,&s); --l;
		v[l].pb(mp(r,s)); v[r].pb(mp(l,-s)); fa[getfa(l)]=getfa(r);
	}
	for (i=1;i<=n;++i) if (!vis[i]) DFS(i);
	for (i=1;i<=q;++i)
	{
		scanf("%d%d",&l,&r); --l; if (getfa(l)!=getfa(r))
		puts("UNKNOWN"); else printf("%lld\n",pfx[r]-pfx[l]);
	}
	return 0;
}

Postscript

立个flag以后一个礼拜至少做两套蓝桥杯真题,希望下个月能堪堪混个省一搞点综测加分什么的

标签:组真题,const,类省赛,CI,蓝桥,int,include,RI,define
From: https://www.cnblogs.com/cjjsb/p/17181798.html

相关文章

  • 蓝桥杯-考勤刷卡
    蓝桥杯-考勤刷卡​​1、问题描述​​​​2、解题思路​​​​3、代码实现​​1、问题描述  小蓝负责一个公司的考勤系统,他每天都需要根据员工刷卡的情况来确定每个员工......
  • 蓝桥杯-超级质数
    蓝桥杯-超级质数​​1、问题描述​​​​2、解题思路​​​​3、代码实现​​1、问题描述  如果一个质数P的每位数字都是质数,而且每两个相邻的数字组成的两位数是质......
  • 2020年蓝桥杯省赛 数字三角形 ------动态规划
    。。。。。。。。。 importjava.util.Scanner;publicclassMain{publicstaticvoidmain(String[]args){Scannerinput=newScanner(System.in......
  • 蓝桥杯 & 青蛙过河(最快贪心) (不用并查集)
      点击查看代码#include<bits/stdc++.h>usingnamespacestd;typedeflonglongll;constintN=1000000+7;lla[N];llb[N];llc[N];lln,x;boolcheck(ll......
  • 蓝桥杯集训资料题
    6296:迷宫描述 一天Extense在森林里探险的时候不小心走入了一个迷宫,迷宫可以看成是由n*n的格点组成,每个格点只有2种状态,.和#,前者表示可以通行后者表示不能通行。同......
  • 蓝桥杯备战日志(Python)20-受伤的皇后-(矩阵搜索、递归)
    原题有一个  的国际象棋棋盘( 行  列的方格图),请在棋盘中摆放  个受伤的国际象棋皇后,要求:任何两个皇后不在同一行。任何两个皇后不在同一列。如果两个皇后在同一条45......
  • 2023.3.1AcWing蓝桥杯集训·每日一题
    今日的知识点为\(BFS\)(广度优先搜素)。\(BFS\)简要介绍下\(BFS\)算法。首先,\(BFS\)算法适用于边权为\(1\)的图论问题。\(BFS\)算法的解题思路也比较固定。确定......
  • P8631 [蓝桥杯 2015 国 AC] 切开字符串 题解
    P8631[蓝桥杯2015国AC]切开字符串题解前言看到这题没有人写题解,就打算写一篇。这也是蒟蒻的第一篇题解。前置知识\(manacher\),\(SA\)。如果不会可以转至mana......
  • 2023.2.27AcWing蓝桥杯集训·每日一题
    复习的知识点为哈希。AcWing840.模拟散列表题目描述维护一个集合,支持如下几种操作:Ix,插入一个数\(x\);Qx,询问数\(x\)是否在集合中出现过;现在要进行\(N\)次操......
  • 蓝桥杯备战日志(Python)19-阅兵方阵&删除字符-(平方和频次统计&字符串字典序)
    阅兵方阵原题X国要参加同盟阅兵活动。主办方要求每个加盟国派出的士兵恰好能组成2个方阵。X国发现弱小的Y国派出了130人的队伍,他们的士兵在行进中可以变换2种队......