今天 6.6 蓝桥杯的国赛结束了,感觉还是有难度的,虽说可以暴力。我这个小蒟蒻可以说感觉能全A的可能也只有 C 题和 D 题了吧,其他都基本上乱蒙或者暴力了。在此和大家分享一下我做题思路和我个人认为的备赛建议。
注:本文代码未必正确,只是我个人当时做题的代码的复现,大家自行分辨。
做题过程
说实话,我之前做 2025 的国赛题目时,第一题填空题真是难爆了,搞得我还下意识以为这次填空可能也会很难。结果看完第一题,发现这居然是高中数列的裂项相消?!还好高考完还没全部把东西还给高中老师,在草稿纸上算完是 6079 就提交了。题目我记得就是算
要求算出 S 化简成最简分数形式后分子分母的和。也许这题很多人都会做吧。
第二个填空是要计算 1-20260606 里,插入任意 0-9 的数在其中且不能出现前导零有多少种方案。感觉有点难数,我想能不能拿一个 set 枚举全部情况算了,虽然可能慢点但是最后应该能有结果的吧?结果我写完放在那里快一个小时了,后面突然搞得我电脑开始闪屏了......有可能是给内存跑满了,给我监考系统都卡掉了。于是只能放弃这道题了。
C 题是给一个 n-1 的 01 串,要构造出来一个最小字典序的 n 排列(排列是 1-n 只能出现一次且只能出现一次),01 串 0 代表两数差的绝对值是偶数,1是奇数。这题我分两种情况第一个放 1 或者 2 ,如果两个都不行那就只能输出 -1 了。其实我原本想着不知道要不要考虑放 2 在第一位的情况的,因为感觉用不上但我又证明不出来到底要不要考虑,于是干脆加上了。用两个变量一个记录当前奇数填到哪个,一个记录偶数填到哪个就好了。如果后续扫整个答案数组发现有个大于 n 的数那说明构造失败了输出 -1 。这样 O(N) 就能解决了。代码写得有点屎......
cpp
#include <bits/stdc++.h>
#define int long long
using namespace std;
void solve(){
int n;
string s;
cin>>n>>s;
vector<int> v1,v2;
v1.push_back(1);
v2.push_back(2);
int j1=3,o1=2;
for(int i=1;i<n;++i){
if(s[i-1]=='1'){
if(v1[i-1]&1){
v1.push_back(o1);
o1+=2;
}else{
v1.push_back(j1);
j1+=2;
}
}else{
if(v1[i-1]&1){
v1.push_back(j1);
j1+=2;
}else{
v1.push_back(o1);
o1+=2;
}
}
}
bool st1=false;
for(int i=0;i<v1.size();++i){
if(v1[i]>n){
st1=true;
break;
}
}
if(!st1){
for(int i=0;i<v1.size();++i){
cout<<v1[i]<<' ';
}
return;
}
int j2=1,o2=4;
for(int i=1;i<n;++i){
if(s[i-1]=='1'){
if(v2[i-1]&1){
v2.push_back(o2);
o2+=2;
}else{
v2.push_back(j2);
j2+=2;
}
}else{
if(v2[i-1]&1){
v2.push_back(j2);
j2+=2;
}else{
v2.push_back(o2);
o2+=2;
}
}
}
bool st2=false;
for(int i=0;i<v2.size();++i){
if(v2[i]>n){
st2=true;
break;
}
}
if(!st2){
for(int i=0;i<v2.size();++i){
cout<<v2[i]<<' ';
}
return;
}
cout<<-1;
}
signed main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int T=1;
//cin>>T;
while(T--){
solve();
}
}
D 题是给定 n,m,k ,一个 n 大小的数组 a ,这个数组 a 是一个环,也就是说索引 1 和 n 是属于相邻的。相邻的两个数之差绝对值大于 k 说明这两个数之间"不稳定"。要求可以从某个地方断开这个环,然后断开后要求出最长的链的长度,这个链里面只能最多有 m 个不稳定的地方。
首先第一个想法就是把环转化成链 嘛,用 2n 的长度把前 n 个数复制到后面,这样每次只处理 n 长度区间就完美实现了从不同位置断开了。断开后要求最长的链,我就想到好像可以二分?二分答案区间在 1-n (最长长度就是 n 了,且长度为 1 对应 m=0 不允许出现任何不稳定且原本 a 数组所有地方都是不稳定的极端情况 ),然后二分答案 mid 去 2n 的数组里去利用双指针维护一个 mid 长度的滑动窗口,用一个变量 cnt 来记录当前这个窗口有多少不稳定的地方,一旦当前这个窗口满足 cnt 不大于 m ,那就说明这个猜测的 mid 长度可行了 。移动的时候只需要让新进来的数和窗口最后一个数计算如果大于 k 就 ++cnt ,前面的数这时也要弹出了,弹出时候如果这个数和他旁边的数也属于一个不稳定的就 --cnt 。个人感觉这样做应该没什么问题,且复杂度应该在 O(NlogN) 应该也不会超时。
cpp
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=2e5+10;
int arr[maxn<<1];
bool chek(int mid,int n,int m,int k){
deque<int> dq;
int cnt=0;
for(int i=1;i<=mid;++i){
if(dq.empty()){
dq.push_back(arr[i]);
continue;
}
if(abs(arr[i]-dq.back())>k) ++cnt;
dq.push_back(arr[i]);
}
if(cnt<=m) return true;
for(int i=mid+1;i<=(n<<1);++i){
if(abs(arr[i]-dq.back())>k) ++cnt;
dq.push_back(arr[i]);
int t=dq.front();
dq.pop_front();
if(abs(t-dq.front())>k) --cnt;
if(cnt<=m) return true;
}
return false;
}
void solve(){
int n,m,k;
cin>>n>>m>>k;
for(int i=1;i<=n;++i){
cin>>arr[i];
arr[i+n]=arr[i];
}
int l=1,r=n;
int ans=0;
while(l<=r){
int mid=(l+r)>>1;
if(chek(mid,n,m,k)){
ans=mid;
l=mid+1;
}else r=mid-1;
}
cout<<ans<<'\n';
}
signed main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int T=1;
//cin>>T;
while(T--){
solve();
}
}
其他题目基本要么暴力要么乱蒙了因为实在不怎么会了。感到有点遗憾的可能就是一个比较明显的是用线段树或者树状数组维护方差和平均数吧。线段树的题目我做得不少了其实,但是维护方差我还没试过而且感觉计算还要平均数,而且题目还是单点修改,感觉树状数组更好吧,做起来有点难。反正我个人当时没什么信心能一下写对感觉很麻烦。而且我记得好像看他题目有 50% 的数据是支持 O(n\^2) 暴力的,20% 数据不涉及修改只查询的。所以写个前缀和拿走 70% 的分其实还能接受吧。
个人建议
这一趟蓝桥杯下来感觉蓝桥杯虽然很多题接受暴力拿点分,但其实能看出来已经越来越喜欢考我们的思维 了。这次的题目我感觉很多都是需要仔细思考拆解题目一步步模拟优化来找到解法的,不是套模板套公式就能完全解决的那种。所以备赛的时候还是尽量多自己做题思考,锻炼一下自己的做题的思维 吧。个人认为形成一套自己做题的方法论还是很重要的,当然适时放弃暴力拿分也很重要。
其次就是我发现好像蓝桥杯很喜欢考二分 ,我做过前几年的国赛题目发现好像都有能用二分 能解决的题目。虽说蓝桥杯好像更爱考动态规划 DP ,但是感觉这个二分的出现频率也太高了。不过我感觉这也比较符合蓝桥杯的出题意图吧。比较蓝桥杯非常想提升一下做题的思维难度不再当暴力杯了,而二分是一个逆向的"猜答案"的方法。我就是完全做多了二分的题目比较敏感所以二分的题目都能很快识别出来。而且涉及二分的题目通常也不会特别难,而 DP 题目有些是真的难到没什么下手的方法了。况且二分如果不做多几题实战很难一下想到,二分掌握不好也很容易写出 bug 。所以我个人认为可以多看看二分的题目也是很好的。如果有兴趣可以看我的一篇关于二分的写法及思考方法的博客:https://blog.csdn.net/h_a_o777oah/article/details/159051684
总得来说大家对于蓝桥杯还是不要掉以轻心吧。拿个国三或许比较简单,但是国二国一实际参赛来看确实已经不这么简单了。