隐藏页面特效

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

1|0Preface


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

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

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

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

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

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


2|0A 裁纸刀


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

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


3|0B 灭鼠先锋


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

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

简单写个代码跑一下答案是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; }

4|0C 求和


不妨设sum=i=1nai,然后枚举每个数,先把sum减去ai,然后用ai乘上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; }

5|0D 选数异或


考虑对于每个数ai求出在它之前且距离它最近的j,满足aiaj=x,这个可以通过一个桶轻松实现,我们记prei=j

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

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; }

6|0E 爬树的甲壳虫


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

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

fn=0fi=(1pi+1)×fi1+pi+1×f0+1   (i[1,n1])

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

#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; }

7|0F 青蛙过河


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

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

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

此时由于所有区间和都相等,因此必然有hi=hi+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; }

8|0G 最长不下降子序列


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

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

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

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

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

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

#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; }

9|0H 扫描游戏


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

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

我们考虑设原点与待排序点的连线与y轴正半轴顺时针旋转形成的角为θ,显然tanθ=xy

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

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

我们用线段树维护区间的xi2+yi2的最小值,每次先查询[lst+1,n]中是否有满足要求的点,没有就查询[1,lst1]

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

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

不过最后转念一想,由于xi2+yi2的范围是2×109的,因此给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; }

10|0I 数的拆分


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

首先一眼先给n分解质因数,然后考虑每个质因数的指数c1,c2,,ck

不难发现如果ci=1的话显然无解,否则我们总可以用一下方法构造出形如x12x23的方案:

  • ci为偶数,则直接让x1=x1×pici2即可
  • ci为奇数,此时有ci3,则让x1=x1×pici32,x2=x2×pi即可

因此我们只要枚举(1018)15范围的质数即可,最后再判断下多余下来的部分是不是平方数或者立方数即可

#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; }

11|0J 推导部分和


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

对于li,ri,si的信息,我们设pfxi为原序列的前缀和数组,则有pfxripfxli1=si

用差分约束的思想,我们可以建一条从li1ri权值为si的边,然后建一条rili1,权值为si的边

然后询问的时候也同理,我们看下qli1qri是否在一个联通块内即可,不在的话显然就是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; }

12|0Postscript


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


__EOF__

本文作者hl666
本文链接https://www.cnblogs.com/cjjsb/p/17181798.html
关于博主:复活的ACM新生,目前爱好仅剩Gal/HBR/雀魂/单机/OSU
版权声明:转载请注明出处
声援博主:欢迎加QQ:2649020702来DD我
posted @   空気力学の詩  阅读(262)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示