十六届蓝桥杯国赛个人题解

1、新型锁

0新型锁 - 蓝桥云课

问题描述

密码学家小蓝受邀参加国际密码学研讨会,为此他设计了一种新型锁,巧妙地融合了数学的严谨性与密码学的安全性。这把锁包含 2025 个连续的数字格,每个格子需填入一个正整数,从而形成一个长度为 2025 的序列 {a1,a2,...,a2025},其中 ai表示第 i 个格子上的数字。

要想解锁,该序列需满足以下条件:任意两个相邻格子中的数字,其最小公倍数(LCM)均为 2025。即对于所有的 i(1≤i≤2024),需满足:LCM(ai,ai+1)=2025现在,请你计算有多少个不同的序列能够解开这把锁。由于答案可能很大,你只需输出其对 10^9+7 取余后的结果即可。

答案提交

这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

运行限制

语言 最大运行时间 最大运行内存
C++ 1s 256M
C 1s 256M
Java 2s 256M
Python3 3s 256M
PyPy3 3s 256M
Go 3s 256M
JavaScript 3s 256M

题解:

题意简述

构造长度为 2025 的正整数序列,满足任意相邻两数的 LCM = 2025,求合法序列总数,答案对 10^9+7 取模。

解题思路

核心结论:能参与构成 LCM(𝑥,𝑦)=2025 的数,一定是 2025 的约数。2025 总共有 15 个约数,状态空间极小,适合 DP。

DP 状态dp[i][j] 表示第 i 位填写第 j 个约数的合法方案数。

初始化 :第一位可填任意约数,dp[1][j] = 1

状态转移 :若 lcm(v[k],v[j]) == 2025,则从上一位状态累加:dp[i][j] += dp[i-1][k],全程取模。

最终答案:累加第 2025 位所有状态的方案数。

复杂度分析

  • 时间复杂度:O(2025×15×15),完全满足运行限制
  • 空间复杂度:O(2025×15)

AC代码

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

const int mod = 1e9 + 7;
// dp[i][j]: 第i位选第j个约数的方案数
int dp[2026][15];
// 存储2025的所有约数
vector<int> v;

// 预处理2025的所有约数
void get_factor() {
    for (int i = 1; i <= 2025; i++) {
        if (2025 % i == 0) v.push_back(i);
    }
}

int main() {
    get_factor();
    int len = v.size();

    // 边界初始化:第一位任意约数都合法
    for (int i = 0; i < len; i++) dp[1][i] = 1;

    // DP递推
    for (int i = 2; i <= 2025; i++) {
        for (int j = 0; j < len; j++) { // 当前位选v[j]
            dp[i][j] = 0;
            for (int k = 0; k < len; k++) { // 上一位选v[k]
                // 满足相邻LCM=2025则转移
                if (lcm(v[j], v[k]) == 2025) {
                    dp[i][j] = (dp[i][j] + dp[i-1][k]) % mod;
                }
            }
        }
    }

    // 统计所有合法结尾方案
    long long ans = 0;
    for (int i = 0; i < len; i++) {
        ans = (ans + dp[2025][i]) % mod;
    }
    cout << ans;
    return 0;
}

2、互质藏卡

0互质藏卡 - 蓝桥云课

问题描述

小蓝整理着阁楼上的旧物,偶然发现了一个落满灰尘的卡片箱。打开箱子,里面整齐地摆放着 17600 张卡片,每张卡片上都写有一个数字,恰好包含了从 1 到 17600 的所有正整数。

儿时的他热衷于收集各种卡牌,数量之多令人咋舌。如今,再次翻阅这些尘封的记忆,小蓝不禁感慨万千。他想起收藏家前辈的箴言:"收藏的魅力在于精粹,而非数量"。于是,他决定从这些卡牌中选取 2025 张,组成一套 "互质藏卡"。

"互质藏卡" 的特点在于:任意两张卡片上的数字之间互质,即它们的最大公约数恒为 1。现在,请你帮小蓝计算,共有多少种不同的选取方案,使得选出的 2025 张卡片满足 "互质藏卡" 的条件。由于答案可能很大,你只需给出其对 10^9+7 取余后的结果即可。

注意:两个选取方案被认为是不同的,当且仅当它们所包含的数字集合不完全相同。即,若存在至少一个数字出现在其中一个集合但不出现在另一个集合中,则这两个方案被视为不同。

答案提交

这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

运行限制

语言 最大运行时间 最大运行内存
C++ 1s 256M
C 1s 256M
Java 2s 256M
Python3 3s 256M
PyPy3 3s 256M
Go 3s 256M
JavaScript 3s 256M

题解:

题目大意

从 1∼17600 中选出 2025 个数,满足任意两个数互质,求方案数,答案对 10^9+7 取模。

核心思路

  1. 互质选数规则 要选出两两互质的数,最优且唯一合法的方式是:

    • 每个质数的幂次集合中选一个数
    • 质数的幂次之间两两互质
    • 总数量恰好为 2025,与题目要求一致
  2. 方案数计算 总方案数 = 所有质数的幂次个数相乘 例如:

    • 质数 2 的幂:2,4,8...≤17600,共 14 个
    • 质数 3 的幂:3,9,27...≤17600,共 8 个
    • 最终把所有质数的幂次个数相乘即为答案
  3. 实现步骤

    1. 遍历 2∼17600,用试除法判断质数
    2. 对每个质数,计算它在范围内的幂次个数
    3. 将所有个数相乘,取模得到答案

代码说明

  • f1(m):试除法判断质数
  • f2(a,b):快速幂计算 a^b
  • 主函数遍历所有数,统计每个质数的合法幂次数量,累乘得到答案

AC代码

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

// 题目范围:数字 1~17600
int n = 17600;
// 答案取模的模数 1e9+7
int mod = 1e9 + 7;

// 函数1:判断一个数是否是质数
// 返回 1 表示是质数,返回 0 表示不是质数
int f1(int m) {
    // 1 不是质数
	if (m == 1)return 0;
    // 2 是质数
	if (m == 2)return 1;
    // 试除法判断质数:从 2 遍历到 sqrt(m)
	for (int i = 2; i <= sqrt(m); i++) {
        // 能被整除,说明是合数
		if (m % i == 0)return 0;
	}
    // 循环结束没找到因子,是质数
	return 1;
}

// 函数2:快速幂,计算 a^b,用于判断质数的幂是否超过范围
ll f2(ll a, ll b) {
	ll ans = 1;
	while (b) {
        // 如果 b 是奇数,把当前 a 乘到结果里
		if (b & 1)ans = ans * a;
        // b 右移一位,等价于除以 2
		b = b >> 1;
        // a 平方
		a = a * a;
	}
	return ans;
}

signed main() {
    // 答案初始化为 1(乘法单位元)
	ll ans = 1;

    // 遍历 2~17600 所有数
	for (int i = 2; i <= 17600; i++) {
        // 统计当前质数 i 在 17600 范围内的幂次个数
		ll cnt = 0;
        // 如果 i 是质数
		if (f1(i)) {
            // 枚举指数 j,计算 i^j
			for (int j = 1;; j++) {
                // 如果 i^j 不超过 17600,计数 +1
				if (f2(i, j) <= 17600)cnt++;
                // 超过范围就停止
				else break;
			}
            // 答案 = 答案 × 当前质数的幂次个数,随时取模防止溢出
			ans = (ans * cnt) % mod;
		}
	}

    // 输出最终答案
	cout << ans;
	return 0;
}

3、数字轮盘

0数字轮盘 - 蓝桥云课

问题描述

"数字轮盘" 是一款益智游戏,基于一个带有指针的圆形轮盘展开。轮盘边缘按顺时针刻有数字 1 至 N,初始时指针指向 1。

游戏分为两阶段:旋转轮盘和恢复轮盘。

第一阶段,将轮盘顺时针旋转 K 次。每次旋转,数字依次后移一位,指针指向的数字随之改变。例如,对于 N=4 的轮盘,初始状态为 1, 2, 3, 4(指针指向 1),旋转一次变为 4, 1, 2, 3(指针指向 4),再旋转一次变为 3, 4, 1, 2(指针指向 3),依此类推。

第二阶段,小蓝需通过操作恢复初始状态,每次操作包含以下两步:

• 第一步:翻转以指针为起点、顺时针方向的前 N−1 个数字的顺序。

• 第二步:翻转除指针外的 N−1 个数字的顺序。

例如,对 N=4,状态为 4, 1, 2, 3(指针指向 4)进行一次操作:

• 第一步:翻转 4, 1, 2,变为 2, 1, 4, 3(指针指向 2)。

• 第二步:翻转 1, 4, 3,变为 2, 3, 4, 1(指针指向 2)。

现在,给定轮盘的数字个数 N 和旋转次数 K,请计算小蓝最少需要几次操作才能恢复初始状态。如果无法恢复初始状态,则输出 −1。

输入格式

输入的第一行包含一个整数 T,表示测试用例的数量。

接下来 T 行,每行包含两个整数 N 和 K,分别表示轮盘上的数字个数和旋转次数。

输出格式

输出共 T 行,每行包含一个整数,表示最少需要的操作次数。如果无法恢复初始状态,则输出 −1。

样例输入

复制代码
2
3 2
4 1

样例输出

评测用例规模与约定

对于 30% 的评测用例,1≤T≤10^2,2≤N≤500,0≤K≤500。

对于 100% 的评测用例,1≤T≤10^5,2≤N≤10^9,0≤K≤10^9。

运行限制

语言 最大运行时间 最大运行内存
C++ 1s 256M
C 1s 256M
Java 2s 256M
Python3 3s 256M
PyPy3 3s 256M
Go 3s 256M
JavaScript 3s 256M

题解:

题目大意

轮盘顺时针旋转 K 次后,使用规定操作恢复初始状态,求最少操作次数。

关键规律推导(核心)

通过模拟操作可以发现一个决定性结论一次完整的恢复操作,等效于让指针顺时针旋转 2 次。

问题瞬间转化为:

找到最小的操作次数 x,使得 2 * x ≡ k (mod n) 即:x 次操作能把指针转回原位。

公式求解

  1. k = 0:已经是初始状态,答案 0
  1. m = n - k
  2. n 为奇数 :一定可以恢复
    • m 偶数 → m / 2
    • m 奇数 → (m + n) / 2
  3. n 为偶数
    • m 偶数 → m / 2
    • m 奇数 → 无法恢复,输出 -1

代码亮点

  • 公式推导,O (1) 求解
  • 全程使用 long long 避免溢出
  • 严格符合输出格式,每组输出换行

AC代码

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;  

signed main() {
    ll t;   
    cin >> t;
    while (t--) { 
        ll n, k;
        cin >> n >> k;  // n:轮盘数字个数,k:顺时针旋转次数
        
        k %= n;         // 旋转 n 次等价于不旋转,取模简化有效旋转次数
        if (k == 0) {   // 旋转 0 次,已经是初始状态,无需操作
            cout << 0 << "\n";
            continue;
        }
        
        ll m = n - k;   // 计算偏移量补数,用于后续奇偶判断
        
        if (n % 2 != 0) {  // 情况 1:n 为奇数 → 一定可以恢复
            if (m % 2 == 0)
                cout << m / 2;        // m 偶数,最少操作次数 m/2
            else
                cout << (m + n) / 2;  // m 奇数,最少操作次数 (m+n)/2
        } else {            // 情况 2:n 为偶数 → 存在无法恢复的情况
            if (m % 2 == 0)
                cout << m / 2;        // m 偶数,可恢复,操作次数 m/2
            else
                cout << -1;           // m 奇数,无法恢复,输出 -1
        }
        
        if (t != 0) cout << "\n";  
    }
}

4、斐波那契字符串

问题描述

斐波那契字符串 S 是由 "0" 和 "1" 所组成的字符串,其生成规则如下:

• S1=0。

• S2=1。

• 对于任意正整数 n(n≥3),Sn​=Sn−2​+Sn−1​("+" 表示字符串拼接)。

例如:S3=01、S4=101、S5=01101。

在斐波那契字符串 S 中,定义逆序对为满足以下条件的整数对 (i,j):

• 1≤i<j≤∣S∣(其中 ∣S∣ 表示 S 的长度)。

• Si=1(第 i 个字符为 "1")并且 Sj=0(第 j 个字符为 "0")。

现在,给定一个正整数 N,请你计算出 SN​ 中所有逆序对 (i,j) 的总数。由于结果可能很大,请输出其对 10^9+7 取余后的值。

输入格式

输入的第一行包含一个整数 T ,表示测试用例的数量。

接下来的 T 行,每行包含一个整数 N,表示要计算的斐波那契字符串的序号。

输出格式

对于每个测试用例,输出一行,包含一个整数,表示 SN​ 中所有逆序对的总数对 10^9+7 取余后的结果。

样例输入

复制代码
2
3
5

样例输出

样例说明

对于 N=3,S3​=01,逆序对总数为 0。

对于 N=5,S5=01101,逆序对为 (2,4)、(3,4),总数为 2。

评测用例规模与约定

对于 20% 的评测用例,1≤T≤20,3≤N≤35。

对于 100% 的评测用例,1≤T≤10^5,3≤N≤10^5。

运行限制

语言 最大运行时间 最大运行内存
C++ 1s 256M
C 1s 256M
Java 2s 256M
Python3 3s 256M
PyPy3 3s 256M
Go 3s 256M
JavaScript 3s 256M

题解:

题目大意

斐波那契字符串定义: S1​=0, S2​=1, Sn​=Sn−2​+Sn−1​(字符串拼接) 逆序对定义:位置 i<j,满足 Si='1' 且 Sj='0'。 给定 N,求 SN​ 的逆序对总数,对 10^9+7 取模。

解题思路

1. 定义三个状态

  • ai:Si 中 0 的数量
  • bi:Si 中 1 的数量
  • ci:Si 中逆序对数量

2. 递推公式(核心)

因为 Sn​=Sn−2​+Sn−1​:

  1. 字符数量: ai=ai−1+ai−2 bi=bi−1+bi−2
  2. 逆序对: ci=ci−1+ci−2+bi−2∗ai−1
    • ci−2:左串内部逆序对
    • ci−1:右串内部逆序对
    • bi−2∗ai−1左串的 1 与右串的 0 形成的跨串逆序对

3. 优化方案

  • 预处理到 10^5,每组查询 O(1)
  • 全程取模防止溢出
  • 时间复杂度 O(10^5+T),可通过 T=10^5 极限数据

代码亮点

  • 预处理递推,效率极高
  • 状态定义清晰,公式直观
  • 适合大数据范围,无超时、无溢出

AC代码

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll; 

const ll mod = 1e9 + 7;
const int MAX = 1e5 + 10;

ll a[MAX];  // a[i]:第i个斐波那契字符串 S_i 中 字符'0'的数量
ll b[MAX];  // b[i]:第i个斐波那契字符串 S_i 中 字符'1'的数量
ll c[MAX];  // c[i]:第i个斐波那契字符串 S_i 中 逆序对的总数量

signed main() {
    // S1 = "0"
    a[1] = 1;  // 0的个数:1
    b[1] = 0;  // 1的个数:0
    c[1] = 0;  // 逆序对:0
    
    // S2 = "1"
    a[2] = 0;  // 0的个数:0
    b[2] = 1;  // 1的个数:1
    c[2] = 0;  // 逆序对:0

    // 从3开始递推到最大数据范围1e5
    for (int i = 3; i <= 1e5; i++) {
        // 逆序对总数 = 左边串逆序对 + 右边串逆序对 + 左边1的个数 × 右边0的个数
        // 左边 = S_{n-2},右边 = S_{n-1}
        c[i] = (c[i-1] + c[i-2] + b[i-2] * a[i-1]) % mod;
        
        // 0的个数:左边0 + 右边0
        a[i] = (a[i-1] + a[i-2]) % mod;
        // 1的个数:左边1 + 右边1
        b[i] = (b[i-1] + b[i-2]) % mod;
    }
    
    ll T;
    cin >> T;
    while (T--) {
        ll n;
        cin >> n;
        cout << c[n] << endl;
    }
    
    return 0;
}
相关推荐
小陈phd1 小时前
多模态大模型学习笔记(四十六)——图像-文本生成(Image-Text Generation):跨模态创作与语义互通
笔记·学习·计算机视觉
噜噜噜阿鲁~1 小时前
python学习笔记 | 11.4、面向对象高级编程-定制类
笔记·python·学习
方也_arkling1 小时前
【Java-Day18】API篇-Arrays
java·算法·排序算法
吴可可1231 小时前
Curve.GetSplitCurves高效分割技巧
算法
爱喝水的鱼丶2 小时前
SAP-ABAP:SAP 简单报表输出开发系列(共6篇) 第五篇:SAP 报表多格式输出:Excel/PDF 批量导出功能实现
学习·性能优化·pdf·excel·sap·abap
凯尔萨厮2 小时前
Hibernate(学习笔记)
笔记·学习·hibernate
lunzi_08262 小时前
【学习笔记】《Python编程 从入门到实践》第5章:if语句、条件测试与列表处理实战
笔记·python·学习
硅谷秋水2 小时前
Qwen-VLA:跨任务、环境与机器人形态的视觉-语言-动作统一建模
人工智能·深度学习·算法·计算机视觉·语言模型·机器人