蓝桥杯多校模拟赛 题解

本人26年国二,讲解备赛过程中做过的模拟赛。

比赛说明:本次比赛由东北大学秦皇岛分校、安徽师范大学、安徽工业大学、安徽农业大学、阜阳师范大学、郑州轻工业大学、武昌首义学院联合举办,为蓝桥杯参赛选手们提供两场免费的蓝桥杯模拟赛。赛制与题型均参考蓝桥杯。

比赛链接:https://ac.nowcoder.com/acm/contest/130226#question

文章目录

  • [蓝桥杯多校模拟赛 题解](#蓝桥杯多校模拟赛 题解)
    • [A. 无限重复序列](#A. 无限重复序列)
    • [B. 子序列数字和整除](#B. 子序列数字和整除)
    • [C. 天气预报](#C. 天气预报)
    • [D. 重复子串计数](#D. 重复子串计数)
    • [E. 反向数字三角形](#E. 反向数字三角形)
    • [F. 手套配对最小最大差值](#F. 手套配对最小最大差值)
    • [G. 优雅字符串最小交换次数](#G. 优雅字符串最小交换次数)
    • [H. 最短全覆盖子序列](#H. 最短全覆盖子序列)

蓝桥杯多校模拟赛 题解

A. 无限重复序列

题意

由字符串 "2026" 无限拼接形成序列,求该序列前 202620262026 项中字符 '2' 的总个数。

思路

序列以 "2026" 为周期循环,周期长度为4,每个周期内包含2个 '2'

  1. 计算完整周期数量:full = num / 4,共贡献 2 * full'2'
  2. 计算剩余字符数 rem = num % 4,统计周期前 rem 位中 '2' 的个数。本题中 202620262026 % 4 = 2,前两位为 "20",额外贡献1个 '2'

最终结果为 2 * (202620262026 / 4) + 1,直接计算即可得到答案。

B. 子序列数字和整除

题意

"2026" 重复2026次形成长度为 4×2026 的数字序列,从中任选3位组成子序列,求满足三位数字之和能被6整除的子序列数量,结果对 10^9+7 取模。

思路

先统计各数字的出现次数:每个周期 "2026" 含1个0、2个2、1个6,重复2026次后,cnt0 = 2026cnt2 = 2*2026cnt6 = 2026

数字0和6模6均为0,数字2模6为2,仅当三元组全为模0、或全为模2时,和能被6整除:

  1. 三个数均为0或6 :任意组合的和都能被6整除,组合数等价于 C(cnt0 + cnt6, 3),可拆分为三个0、两0一六、一零两六、三个6四种情况分别计算。
  2. 三个数均为2 :和为 2×3=6,能被6整除,组合数为 C(cnt2, 3) = C(2×2026, 3)

通过组合数公式结合逆元处理取模,将两种情况的结果相加模 1e9+7 即可。

正解代码

cpp 复制代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+9,M=1e9+7;
int n,m,a[N],ans,cnt;
//逆元 a/b%M = a*ksm(b,m-2)
int ksm(int a,int b){
    int res=1;
    while(b){
        if(b&1)res=res*a%M;
        b>>=1;
        a=a*a%M;
    }
    return res;
}
int C(int a,int b){
    if(b==1)return a;
    if(b==2)return a*(a-1)%M*ksm(2,M-2)%M;
    if(b==3)return a*(a-1)%M*(a-2)%M*ksm(6,M-2)%M;
    return 1;
}
signed main(){
    ans+=C(2026,3)%M;
    ans+=C(2026,1)%M*C(2026,2)%M;
    ans+=C(2026*2,3)%M;
    ans+=C(2026,1)%M*C(2026,2)%M;
    ans+=C(2026,3)%M;
    
    cout<<ans%M;
    
    return 0;
}

C. 天气预报

题意

给定连续n天的天气类型,预报规则为:

  • 第1天预报天气类型1。
  • 前一天预报正确则当天预报不变;预报错误则统计此前所有天中出现次数最多的天气(次数相同时选首次出现最晚的)作为当天预报。
    求预报成功的总天数。

思路

线性模拟预报过程,实时维护当前最优天气,时间复杂度O(n):

  1. 用数组 cnt[x] 记录天气x的出现次数,ti[x] 记录天气x首次出现的天数。
  2. 维护变量 cur 表示当前出现次数最多、且首次出现最晚的天气,mx 记录当前最高出现次数。
  3. 变量 last 表示当天的预报值,初始为1。
  4. 遍历每天的天气:
    • 更新当前天气的计数与首次出现时间,若该天气刷新最高次数,或次数持平但首次出现更晚,则更新 cur
    • 若预报值 last 与当天天气一致,答案+1;否则将次日预报值更新为 cur

正解代码

cpp 复制代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+9;
int n,m,a[N],ans,cnt[N],ti[N];
bool st[N];

signed main(){
    cin>>n;
    int x,mx=0,last=1,cur;
    bool fg=0;
    for(int i=1;i<=n;i++){
        cin>>x;
        if(!st[x]){st[x]=1;ti[x]=i;}
        cnt[x]++;
        if(cnt[x]>mx||(cnt[x]==mx&&ti[x]>ti[cur])){
            mx=cnt[x];
            cur=x;
        }
        if(last==x){
            //cout<<x<<' ';
            ans++;
        }
        else{
            last=cur;
        }
        
    }
    cout<<ans;
    
    return 0;
}

D. 重复子串计数

题意

给定长度为n、仅由F/B/L/R组成的字符串,统计出现次数大于1的长度为k的连续子串的数量。

思路

采用哈希统计思路,实现简单且适配题目k≤10的数据范围:

  1. 遍历字符串,截取所有长度为k的连续子串。
  2. 用哈希表记录每个子串的出现次数。
  3. 遍历哈希表,统计出现次数大于1的子串数目。

正解代码

cpp 复制代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+9;
int n,m,a[N],ans,cnt,k;
map<string,int>mp;
string s;
signed main(){
    cin>>n>>k>>s;
    for(int i=0;i+k<n;i++){
        string s1=s.substr(i,k);
        mp[s1]++;
    }
    for(auto t:mp){
        if(t.second>1)ans++;
    }
    cout<<ans;
    
    return 0;
}

E. 反向数字三角形

题意

已知n和最终数字和sum,原始序列是1~n的排列。将序列相邻两项相加得到新序列,重复操作直到只剩一个数等于sum。求满足条件的字典序最小的原始排列。

思路

该过程本质是杨辉三角加权求和:第i个位置的数最终会乘以系数 C(n-1, i-1)(组合数),所有项的加权和即为最终sum。问题转化为寻找1~n的排列,使得加权和等于sum且字典序最小。

采用深度优先搜索(DFS)求解:

  1. 预处理计算n-1阶杨辉三角的系数数组。
  2. 按位置从左到右枚举排列,从小到大尝试未使用的数字,累加当前加权和。
  3. 剪枝:若当前加权和已超过sum,直接回溯。
  4. 由于按从小到大枚举数字,第一个找到的合法排列即为字典序最小的解,找到后直接输出并退出。

结合剪枝后可高效通过n≤12左右的数据规模。

正解代码

cpp 复制代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+9;
int n,m,sum,mi=13,c[16];
int p[16];
bool st[15];
//暴力可排序 或直接倒着枚举第一个=sum即为最大
/* 杨辉三角模板
vector<int> coef(n + 1);
coef[0] = 1;
for(int i = 1; i <= n; i++) {
    coef[i] = coef[i - 1] * (n - i) / i;
}
*/

void dfs(int u,int cnt){
    if(cnt>sum)return ;
    if(u==n){
        if(cnt==sum){
            for(int i=0;i<n;i++)cout<<p[i]<<' ';
            exit(0);
        }
        return ;
    }
    for(int i=1;i<=n;i++){
        if(!st[i]){
            st[i]=1;
            p[u]=i;
            dfs(u+1,cnt+i*c[u]);
            st[i]=0;
            p[u]=0;
        }
    }
    
}
signed main(){
    cin>>n>>sum;
    if(n==1){
        cout<<1;
        return 0;
    }
    c[0]=1;
    for(int i=1;i<n;i++)c[i]=c[i-1]*(n-i)/i;
    dfs(0,0);
    
    
    
    return 0;
}

F. 手套配对最小最大差值

题意

n只左手手套、m只右手手套各有尺码,配对丑陋程度为尺码差的绝对值。要求配对尽可能多的手套,并使得所有配对中的最大丑陋程度最小,求该最小值。

思路

典型的"最大值最小化"问题,采用二分答案 + 贪心验证求解:

  1. 二分答案 :二分枚举最大允许的差值x,判断能否配对满 min(n,m) 副手套。
  2. 贪心验证
    • 将左右手套尺码分别从小到大排序,保证左手为数量较少的一侧。
    • 双指针遍历:若当前左右手套差值≤x则配对成功,双指针同时右移;否则右移右手指针尝试更大的右手尺码。
    • 若最终配对数等于左手总数,则x可行。
  3. 二分边界为0到尺码最大值差,最终得到最小的可行x。

时间复杂度为O((n+m)log(max_size)),适配n,m≤1e5的数据范围。

正解代码

cpp 复制代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+9;
int n,m,a[N],b[N],ans;
bool ck(int x){
    int cnt=0;//配对数
    int i=1,j=1;
    for(;i<=n&&j<=m;j++){
        if(abs(a[i]-b[j])<=x){
            i++;
            cnt++;
        }
    }
    return cnt>=n;
}
signed main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++)cin>>a[i];
    for(int i=1;i<=m;i++)cin>>b[i];
    if(n>m){
        for(int i=1;i<=n;i++)swap(a[i],b[i]);
        swap(n,m);
    }
    sort(a+1,a+1+n);
    sort(b+1,b+1+m);
    
    int l=0,r=1e9+7;
    while(l<r){
        int mid=(l+r)/2;
        if(ck(mid))r=mid;
        else l=mid+1;
    }
    cout<<l;
    
    
    

    return 0;
}

G. 优雅字符串最小交换次数

题意

字符串仅包含0、1、2,定义"优雅"为没有相邻相同的字符。每次可交换相邻字符,求将字符串变为优雅的最少操作次数,无解输出-1。

思路

暴力思路(30%)

枚举所有合法的优雅排列,计算原串变为该排列的相邻交换次数,取最小值。但n=400时排列数过多,无法通过全量数据。

正解:三维动态规划

  1. 无解判定 :若任意字符的数量超过 (n+1)/2,则无法构造优雅字符串,直接返回-1。
  2. 核心性质:相同字符的相对顺序不会改变最少交换次数,最优解中同字符的相对顺序与原串一致。可预处理每个字符在原串中的位置列表。
  3. DP状态定义dp[i][j][k][c] 表示使用i个0、j个1、k个2,且最后一个字符为c(c∈{0,1,2})时的最少交换次数。
  4. 状态转移:枚举下一个放置的字符(不能与末尾字符相同),计算将对应字符移动到当前末尾的新增代价(已选字符中位置大于当前字符原位置的数量),更新dp值。
  5. 最终答案:取用完所有字符的三个状态中的最小值。

总状态数约为O(cnt0×cnt1×cnt2×3),n=400时可在合理时间内通过。

H. 最短全覆盖子序列

题意

给定长度为n、数值在1 ~ k之间的数列,支持两种操作:修改某个位置的数值;查询包含所有1~k数值的最短连续子序列的长度,无解输出-1。

思路

暴力解法(50%)

每次查询时使用双指针(滑动窗口)算法:维护窗口内各数字的出现次数与种类数,右指针扩展窗口,种类数达到k后左指针收缩窗口,记录最小窗口长度。单次查询O(n),总复杂度O(mn),适合小数据场景。

暴力代码

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
const int N=5010;
int a[N];
int n,k,m;
void f(){
    unordered_map<int,int>mp;
    vector<bool>st(n+1);
    int l=1,ans=1e9;
    for(int r=1;r<=n&&l<=n;r++){
        mp[a[r]]++;
        while(mp.size()==k){
            ans=min(ans,r-l+1);
            mp[a[l]]--;
            if(!mp[a[l]])mp.erase(a[l]);
            l++;
        }
    }
    if(ans==1e9)cout<<-1;
    else cout<<ans;
    cout<<'\n';
    
}
int main(){
    
    cin>>n>>k>>m;
    for(int i=1;i<=n;i++)cin>>a[i];
    while(m--){
        int op,p,v;
        cin>>op;
        if(op==1){
            cin>>p>>v;
            a[p]=v;
        }
        else f();
    }
    
    
    return 0;
}

正解:线段树 + 状态压缩

相关推荐
依然鸣1 小时前
蓝桥杯多校模拟赛(第二场)题解
数据结构·c++·经验分享·学习·算法·图论
LccKyI1 小时前
C# 零基础学习 Day01|基础概念梳理 + 思维导图总结
开发语言·笔记·学习·c#
AKA__Zas1 小时前
芝士算法(位运算)
java·开发语言·算法·leetcode·学习方法
艾莉丝努力练剑1 小时前
【MYSQL】MYSQL学习的一大重点:视图
android·服务器·数据库·学习·mysql·面试·视图
wabs6661 小时前
关于图论【Kruskal算法|卡码网53.寻宝的思考】
数据结构·算法·图论·卡码网
hans汉斯2 小时前
人工智能与机器人研究|面向无标签数据的三维场景语义理解方法研究
人工智能·神经网络·算法·信息可视化·cnn·机器人
艾莉丝努力练剑2 小时前
【MYSQL】MYSQL学习的一大重点:事务(下)- InnoDB 事务隔离性原理(MVCC 视角)
android·数据库·b树·sql·学习·mysql·面试
奇树谦2 小时前
ZeroMQ 详解:轻量级高性能消息通信框架及 C++ 使用示例
开发语言·c++
不相心 -w-2 小时前
现代C++——智能指针
开发语言·c++