记录54
#include<bits/stdc++.h>
using namespace std;
struct node{
bool q;
int v;
}a[100010]={};
bool vis[100010]={};
int main(){
int n,s;
cin>>n>>s;
for(int i=1;i<=n;i++) cin>>a[i].q>>a[i].v;
int ans=0,en=1,d=1,k=1;
while(s>=1&&s<=n){
if(a[s].q==1){
//printf("来到点%d,能量:%d\n",s,en);
if(vis[s]==0){
if(en>=a[s].v){
ans++;
vis[s]=1;
}
}
}
else{
//printf("来到点%d,能量:%d\n",s,en);
d=++d%2;
en+=a[s].v;
}
if(d) s+=en;
else s-=en;
k++;
if(k>10000000) break;//纯邪修:摸准TLE时间
}
cout<<ans;
return 0;
}
题目传送门
https://www.luogu.com.cn/problem/P10132
突破点
以 1 的起始能量向右弹跳。 如果 Bessie 的能量为 k,则她将弹跳至距当前位置向前距离 k 处进行下一次弹跳。
👉能量就是运动的距离
一个数值为 v 的跳板会使 Bessie 的能量增加 v 并反转她的方向。
👉跳板改变运动方向并增加移动距离
陆在炮击目标上不会改变 Bessie 的能量和方向。被击破的炮击目标将保持击破状态,Bessie 依然可以该炮击目标上弹跳,同样不会改变能量和方向。
👉炮击目标,到达目标看是否被击破,然后继续移动
输出一个整数,为将被击破的炮击目标数量。
👉求炮击目标数量
思路
- 构建地图 👉 多数据绑定在一起,结构体
- 到达炮击点,判断是否击碎
- 到达跳板,反转方向,增加能量
代码简析
#include<bits/stdc++.h>
using namespace std;
struct node{
bool q;
int v;
}a[100010]={};
bool vis[100010]={};
int main(){
int n,s;
cin>>n>>s;
for(int i=1;i<=n;i++) cin>>a[i].q>>a[i].v;
int ans=0,en=1,d=1,k=1;
while(s>=1&&s<=n){
....
}
return 0;
}
a[100010]={} 👉 存放数轴的情况 q 为跳板或者目标 v为跳板或炮击目标的数值
bool vis[100010]={}; 👉 判断是否访问过
int n,s; 👉 其中 N 为数轴的长度,S 为 Bessie 的位置
int ans=0(被击破的炮击目标数量),en=1(能量),d=1(方向,右1左0),k=1;(程序执行步长)
while(s>=1&&s<=n){} 👉 在地图里面,没有越界
#include<bits/stdc++.h>
using namespace std;
struct node{
bool q;
int v;
}a[100010]={};
bool vis[100010]={};
int main(){
int n,s;
cin>>n>>s;
for(int i=1;i<=n;i++) cin>>a[i].q>>a[i].v;
int ans=0,en=1,d=1,k=1;
while(s>=1&&s<=n){
if(a[s].q==1){
//printf("来到点%d,能量:%d\n",s,en);
if(vis[s]==0){
if(en>=a[s].v){
ans++;
vis[s]=1;
}
}
}
else{
...
}
...
}
cout<<ans;
return 0;
}
if(a[s].q==1){} 👉 如果是炮击目标
if(vis[s]==0){} 👉 判断是否访问过,没访问过就计数,然后标记为访问
else{} 👉 如果是跳板
#include<bits/stdc++.h>
using namespace std;
struct node{
bool q;
int v;
}a[100010]={};
bool vis[100010]={};
int main(){
int n,s;
cin>>n>>s;
for(int i=1;i<=n;i++) cin>>a[i].q>>a[i].v;
int ans=0,en=1,d=1,k=1;
while(s>=1&&s<=n){
if(a[s].q==1){
//printf("来到点%d,能量:%d\n",s,en);
if(vis[s]==0){
if(en>=a[s].v){
ans++;
vis[s]=1;
}
}
}
else{
//printf("来到点%d,能量:%d\n",s,en);
d=++d%2;
en+=a[s].v;
}
if(d) s+=en;
else s-=en;
k++;
if(k>10000000) break;//纯邪修:摸准TLE时间
}
cout<<ans;
return 0;
}
d=++d%2; 👉 借助自加后取余来实现方向(右1左0)的改变
en+=a[s].v; 👉 能量增加
if(d) s+=en; 👉 往数轴右边移动后的位置
else s-=en; 👉 往数轴左边移动后的位置
k++; 👉 程序执行的步长
if(k>10000000) break; 👉 防止转圈圈,在一个环形图里出不来,通过限定时间跳出
补充
CSP竞赛1秒程序执行步数深度解析
一、理论上限(硬件极限)
CPU主频视角
主频3GHz:每秒钟30亿个时钟周期
单周期指令(简单加减、位运算):3×10⁹次/秒
多周期指令(乘法、除法、内存访问):5×10⁸~1×10⁹次/秒
结论:理论上1秒可执行数十亿次简单指令,但竞赛中远达不到。
二、CSP-OJ实测性能(竞赛关键)
主流OJ平台数据(C++)
操作类型 1秒安全次数 极限次数 说明 简单整数加减 1×10⁸ 2×10⁸ 纯寄存器运算 整数乘/除 5×10⁷ 1×10⁸ 需多个时钟周期 取模运算 % 3×10⁷ 5×10⁷ 除法变体,较慢 内存访问(数组) 2×10⁷ 3×10⁷ 缓存不命中更慢 分支判断 if 8×10⁷ 1.5×10⁸ 分支预测失败惩罚 函数调用 5×10⁷ 8×10⁷ 栈操作开销 IO操作 1×10⁵ 5×10⁵ <stdio.h>比<iostream>快5倍CSP-J安全阈值:1×10⁸次简单操作(考虑OJ负载、评测机性能波动)
极限阈值:2×10⁸次(可能超时,风险极高)
三、时间复杂度与安全数据范围
C++(CSP-J)
复杂度 1秒安全上限 典型题型 备注 O(n) n ≤ 1×10⁸ 遍历数组 常数极小 O(n log n) n ≤ 5×10⁶ 排序、set log₂n≈22 O(n√n) n ≤ 2×10⁵ 分块 √n≈450 O(n²) n ≤ 10⁴ 二维DP 1e4²=1e8 O(n³) n ≤ 300 三重循环 300³=2.7e7 O(2ⁿ) n ≤ 25 子集枚举 2²⁵=3.3e7 O(n!) n ≤ 11 全排列 11!=4e7 经验公式:1秒 ≈ 1×10⁸次简单操作,复杂操作按3-5倍折算。
四、Python vs C++ 性能差距
语言 1秒安全次数 O(n)上限 O(n²)上限 备注 C++ 1×10⁸ 1×10⁸ 1×10⁴ 编译型,机器码 Python 2×10⁶ 2×10⁶ 1.5×10³ 解释型,CSP-J可能受限 结论:Python选手需选择更优算法才能通过。
五、CSP-J官方说明(核心要点)
根据牛客博客(CSP官方性质):
"1000毫秒内最大循环次数不要超过10^8(10^8有点悬,10^7绝对不超时)"
官方建议:
稳妥值:10⁷次(绝对安全)
冒险值:10⁸次(可能超时,看常数)
常数因子:每个循环内若有3次以上取模/乘法,按3×10⁷计算
六、影响实际速度的隐藏因素
1. 评测机负载
高峰期:同一台服务器跑多个评测,性能下降20%-50%
本地测试:0.8秒的程序,OJ上可能1.2秒超时
2. 内存访问模式
连续访问(数组遍历):快,缓存友好
随机访问(链表、map):慢,缓存不命中率高
跨度访问(二维数组列遍历):慢,CPU预取失效
3. 编译优化
O2优化:速度提升2-5倍
O2未开:循环次数需除以5估算
4. 指令效率
整数运算:最快
分支判断:中等(预测失败惩罚10-20周期)
除法/取模:慢(30周期)
浮点运算:慢(10-30周期)
七、竞赛实用估算方法
步骤1:计算理论操作数
for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { sum += a[i][j]; // 1次加法 + 1次内存访问 } } // 总操作数 = 2 * n²步骤2:折算为简单操作
取模/乘法 × 3 除法 × 5 浮点 × 2步骤3:对比安全阈值
if (总操作数 > 1e8) → 必然TLE if (总操作数 > 5e7) → 风险极高 if (总操作数 > 1e7) → 基本安全
八、一句话总结
CSP-J中C++程序1秒 ≈ 1×10⁸次简单操作(极限),稳妥值是5×10⁷,保险值是1×10⁷。Python选手按1/10估算。写完代码先算循环次数,再对比阈值,否则"见祖宗"。
1310

被折叠的 条评论
为什么被折叠?



