C/C++蓝桥杯算法真题打卡(Day4)

news/2025/3/9 19:15:35/

一、P11041 [蓝桥杯 2024 省 Java B] 报数游戏 - 洛谷

算法代码:

#include<bits/stdc++.h>
using namespace std;// 计算第 n 个满足条件的数
long long findNthNumber(long long n) {long long low = 1, high = 1e18; // 二分查找范围while (low < high) {long long mid = (low + high) / 2;// 计算 mid 以内 20 或 24 的倍数的个数long long count = mid / 20 + mid / 24 - mid / 120;if (count < n) {low = mid + 1;} else {high = mid;}}return low;
}int main() {long long n = 202420242024;long long result = findNthNumber(n);cout << result << endl;return 0;
}

代码说明

  1. 二分查找

    • 通过二分查找确定第 n 个满足条件的数。

    • 查找范围从 1 到 1e18(足够大的数)。

  2. 容斥原理

    • mid / 20:计算 mid 以内 20 的倍数的个数。

    • mid / 24:计算 mid 以内 24 的倍数的个数。

    • mid / 120:减去 20 和 24 的最小公倍数的个数(避免重复计算)。

  3. 时间复杂度

    • 二分查找的时间复杂度为 O(log N),效率非常高。

二、P8792 [蓝桥杯 2022 国 A] 最大公约数 - 洛谷

算法代码: 

#include<bits/stdc++.h>
#define int long long
#define lc 2*k
#define rc 2*k+1
using namespace std;
int n,a[1234567],cnt;
struct nord{int l,r,mx;
}t[1234567];
void build(int k,int l,int r){//建树t[k].l=l,t[k].r=r;if (l==r){t[k].mx=a[l];return ;}int mid=(l+r)/2;build(lc,l,mid);build(rc,mid+1,r);t[k].mx=__gcd(t[lc].mx,t[rc].mx);
}
int ask(int k,int l,int r){//求区间gcdif (l<=t[k].l&&r>=t[k].r) return t[k].mx;int ans=0;int mid=(t[k].l+t[k].r)/2;if (l<=mid) ans=__gcd(ans,ask(lc,l,r));if (r>mid) ans=__gcd(ans,ask(rc,l,r));return ans;
}
main(){cin>>n;for (int i=1;i<=n;i++) cin>>a[i],cnt+=(a[i]==1?1:0);build(1,1,n);if (cnt){//特殊情况cout <<n-cnt;return 0;}int ans=1e9;int i=1;for (int j=1;j<=n;j++){//枚举while (i<j&&ask(1,i+1,j)==1) i++;//一直往前走if (ask(1,i,j)==1) ans=min(ans,j-i);//成功了}if (ans==1e9) cout <<-1;//不合法else cout <<n+ans-1;return 0;
}

        这段代码的主要功能是解决一个与区间 GCD(最大公约数)相关的问题。以下是代码的思路和逻辑分析:


代码思路

1. 问题描述

  • 给定一个长度为 n 的数组 a

  • 目标是找到一个最短的区间 [i, j],使得该区间内所有元素的 GCD 为 1。

  • 如果存在这样的区间,输出将整个数组变为全 1 的最小操作次数;否则输出 -1。

2. 核心逻辑

  • 特殊情况处理

    • 如果数组中已经有 cnt 个 1,则直接输出 n - cnt,因为只需要将其他元素变为 1 即可。

  • 区间 GCD 计算

    • 使用线段树维护区间 GCD,支持快速查询任意区间的 GCD。

  • 滑动窗口枚举

    • 使用双指针 i 和 j 枚举区间 [i, j]

    • 如果区间 [i, j] 的 GCD 为 1,则尝试缩小窗口(移动 i)以找到更短的区间。

    • 记录满足条件的最短区间长度。

  • 结果计算

    • 如果找到满足条件的区间,输出 n + ans - 1(将整个数组变为全 1 的最小操作次数)。

    • 否则输出 -1。


代码逻辑分析

1. 线段树构建

  • build 函数用于构建线段树,每个节点存储区间的左右边界和区间 GCD。

  • 递归地将数组划分为左右子区间,直到区间长度为 1。

  • 区间 GCD 通过 __gcd 函数计算。

2. 区间 GCD 查询

  • ask 函数用于查询区间 [l, r] 的 GCD。

  • 如果当前节点区间完全包含在查询区间内,则直接返回节点值。

  • 否则递归查询左右子区间,并计算 GCD。

3. 滑动窗口枚举

  • 使用双指针 i 和 j 枚举区间。

  • 如果区间 [i, j] 的 GCD 为 1,则尝试缩小窗口(移动 i)。

  • 记录满足条件的最短区间长度 ans

4. 结果计算

  • 如果找到满足条件的区间,输出 n + ans - 1

  • 否则输出 -1。


代码优化建议

  1. 变量命名

    • 变量名如 lcrcnord 等不够直观,建议改为更具描述性的名称,如 left_childright_childNode 等。

  2. 代码可读性

    • 添加注释,解释关键逻辑和变量的含义。

    • 使用空格和换行符提高代码的可读性。

  3. 边界条件处理

    • 确保数组下标从 1 开始,避免越界问题。

    • 在滑动窗口枚举时,注意 i 和 j 的移动条件。

  4. 时间复杂度

    • 线段树构建的时间复杂度为 O(n)。

    • 区间查询的时间复杂度为 O(log n)。

    • 滑动窗口枚举的时间复杂度为 O(n log n)。

    • 总体时间复杂度为 O(n log n),效率较高。


修正后的代码

#include<bits/stdc++.h>
#define int long long
using namespace std;const int MAXN = 1234567;
int n, a[MAXN], cnt;struct Node {int l, r, mx;
} t[MAXN * 4];// 构建线段树
void build(int k, int l, int r) {t[k].l = l, t[k].r = r;if (l == r) {t[k].mx = a[l];return;}int mid = (l + r) / 2;build(2 * k, l, mid);build(2 * k + 1, mid + 1, r);t[k].mx = __gcd(t[2 * k].mx, t[2 * k + 1].mx);
}// 查询区间 GCD
int ask(int k, int l, int r) {if (l <= t[k].l && r >= t[k].r) return t[k].mx;int ans = 0;int mid = (t[k].l + t[k].r) / 2;if (l <= mid) ans = __gcd(ans, ask(2 * k, l, r));if (r > mid) ans = __gcd(ans, ask(2 * k + 1, l, r));return ans;
}signed main() {cin >> n;for (int i = 1; i <= n; i++) {cin >> a[i];cnt += (a[i] == 1 ? 1 : 0);}build(1, 1, n);// 特殊情况:数组中已经有 1if (cnt) {cout << n - cnt << endl;return 0;}int ans = 1e9;int i = 1;// 滑动窗口枚举for (int j = 1; j <= n; j++) {while (i < j && ask(1, i + 1, j) == 1) i++;if (ask(1, i, j) == 1) ans = min(ans, j - i + 1);}// 输出结果if (ans == 1e9) cout << -1 << endl;else cout << n + ans - 1 << endl;return 0;
}

总结

        这段代码通过线段树和滑动窗口的结合,高效地解决了区间 GCD 问题。修正后的代码提高了可读性和健壮性,同时保留了原有的高效逻辑。


http://www.ppmy.cn/news/1577887.html

相关文章

使用ASIWebPageRequest库编写Objective-C下载器程序

使用 ASIWebPageRequest 库编写 Objective-C 下载器程序是一个简单且高效的方式来处理 HTTP 请求。在 ASIHTTPRequest 和 ASIWebPageRequest 中&#xff0c;ASIWebPageRequest 是专门用于下载网页及其资源的库。 1. 安装 ASIWebPageRequest 首先&#xff0c;你需要安装 ASIHT…

linux 设置tomcat开机启动

在Linux系统中&#xff0c;要配置Tomcat开机自启动&#xff0c;可以创建一个名为 tomcat.service 的 systemd 服务文件&#xff0c;并将其放置在 /etc/systemd/system/ 目录下。以下是一个基本的服务文件示例&#xff0c;假设Tomcat安装在 /usr/local/tomcat 路径下&#xff1a…

雷池WAF的为什么选择基于Docker

Docker 是一种开源的容器化平台&#xff0c;可以帮助开发人员将应用程序及其所有依赖项打包到一个称为容器的独立、可移植的环境中。Docker 的核心概念包括以下几点&#xff1a; 容器&#xff1a;Docker 使用容器来封装应用程序及其依赖项&#xff0c;使其能够在任何环境中都能…

如何确保爬虫遵守1688的使用协议

在使用爬虫技术调用1688开放平台的API接口时&#xff0c;确保爬虫遵守平台的使用协议至关重要。这不仅有助于避免法律风险&#xff0c;还能确保数据获取行为的合规性和道德性。以下是确保爬虫遵守1688使用协议的具体方法和注意事项&#xff1a; 一、遵守法律法规 合法使用数据…

后 Safe 时代:多签钱包安全新范式与防范前端攻击的新思路

时间轴 2025 年 2 月 21 日&#xff1a;Bybit 多签钱包被攻击&#xff0c;15 亿美金通过「合法」签名交易流出。 链上追踪&#xff1a;资金转入匿名地址并分拆混币&#xff0c;攻击者与部分验证节点存在潜在关联。 事后分析&#xff1a;安全审计发现攻击者利用 Safe 前端的供…

深入理解Tomcat的Request复用机制及其风险

深入理解Tomcat的Request复用机制及其风险 前言一、什么是Request复用机制&#xff1f;二、Request复用的好处三、Request复用的风险四、如何优化Request复用的机制&#xff1f;总结 前言 在高并发的Web应用中&#xff0c;性能优化是每个开发者需要关注的核心问题之一。为了提…

进制的理解与转换

二进制&#xff08;binary&#xff09;是在数学和数字电路中以2为基数的记数系统&#xff0c;这一系统中&#xff0c;通常用两个不同的符号0和1来表示数值。 基本概念 位&#xff08;bit&#xff09;&#xff1a;二进制数据中的基本单位&#xff0c;每一位只能是0或1。在计算机…

软考中级-数据库-3.3 数据结构-树

定义:树是n(n>=0)个结点的有限集合。当n=0时称为空树。在任一非空树中,有且仅有一个称为根的结点:其余结点可分为m(m>=0)个互不相交的有限集T1,T2,T3...,Tm…,其中每个集合又都是一棵树,并且称为根结点的子树。 树的相关概念 1、双亲、孩子和兄弟: 2、结点的度:一个结…