1 条题解
-
0
做题时一定要注意数据范围 数据范围的不同可能导致代码的某些地方不同 题目数据已加强再次交原来的码看会不会被卡掉
第一种做法 暴力枚举
也是赛时写得最多的做法: 用两层循环 分别枚举 和 找到满足的区间 然后答案加一(这里用了前缀和做了预处理)
前缀和
for(int i = 1; i <= n; i++) { sum[i] = sum[i - 1] + a[i]; }
记录的是前 项的和 例如 代表前 个数的和 因此我们如果想要算区间 的和 我们可以用前缀和数组表示:
该解法的时间复杂度为 因此过不了该题
#include<stdio.h> long long int a[100005], sum[100005]; int main() { int n, k; scanf("%d%d",&n,&k); for(int i = 1; i <= n; i++) { scanf("%lld",&a[i]); } for(int i = 1; i <= n; i++) { sum[i] = sum[i - 1] + a[i]; } long long cnt = 0; for(int i = 1; i <= n; i++) { for(int j = i; j <= n; j++) { if(sum[j] - sum[i - 1] >= k) cnt++; } } printf("%lld",cnt); return 0; }
第二种做法 双指针
也是第一种做法的优化: 第一种做法我们是两层循环枚举 和 找答案 那我们是否可以通过枚举 或 来找到答案? 我们发现通过前缀和的维护,我们要找的和是单调的,比如我们通过 找 假如找到了第一个满足条件的 那后面的 都将会满足条件 因此答案加上
例如 当 时 我们发现 区间 的和已经大于等于 那么同理区间和区间 也将会满足 则答案加上 也就是
代码如下
#include<stdio.h> long long int a[100005], sum[100005]; int main() { int n, k; scanf("%d%d",&n,&k); for(int i = 1; i <= n; i++) { scanf("%lld",&a[i]); } for(int i = 1; i <= n; i++) { sum[i] = sum[i - 1] + a[i]; } long long cnt = 0; int r = 1; for(int l = 1; l <= n; l++) { while(sum[r] - sum[l - 1] < k && r <= n) r++; if(r > n) break; cnt += (n - r + 1); } printf("%lld",cnt); return 0; }
核心代码
for(int l = 1; l <= n; l++) { while(sum[r] - sum[l - 1] < k && r <= n) { r++; } if(r > n) break; cnt += (n - r + 1); }
我们通过枚举 来找到第一个满足的 如果找到 那么答案加上 同时我们 的 也不需要变化
如果没找到 则 那么以后的任何区间都不会满足条件 则程序结束
该做法的思想为双指针 由于我们要找的数据具有单调性 所以当 向右移动时 只会向右或不动 所以我们可以来固定 找
第三种做法 二分查找
先了解二分查找可以干什么 它可以在一组有序(升序/降序)的数据中查找一个元素,它是一种效率较高的查找方法。 例如
1 4 7 10 11 20 25 47
我们可以通过二分查找 找到大于 或 大于等于 某个元素的第一个下标 例如我们可以用二分查找到大于 的第一个下标为 也可以用二分查找找到大于等于 的第一个下标为
由于我们的前缀和数组是单调(升序)的 因此我们可以枚举 通过二分查找找到
代码如下
#include<stdio.h> long long int a[100005], sum[100005]; int main() { int n, k; scanf("%d%d",&n,&k); for(int i = 1; i <= n; i++) { scanf("%lld",&a[i]); } for(int i = 1; i <= n; i++) { sum[i] = sum[i - 1] + a[i]; } long long cnt = 0; for(int i = 1; i <= n; i++) { int l = 1, r = n, mid; while(l < r) { mid = (l + r) / 2; if(sum[mid] >= sum[i - 1] + k) r = mid; else l = mid + 1; } if(sum[l] - sum[i - 1] >= k) cnt += (n - l + 1); } printf("%lld",cnt); return 0; }
核心代码
while(l < r) { mid = (l + r) / 2; if(sum[mid] >= sum[i - 1] + k) r = mid; else l = mid + 1; }
因为前缀和数组代表的是前 项的和 因此想要从 开始 找到 右边的第一个 我们要找大于等于 的第一个数
时间复杂度为
- 1
信息
- ID
- 390
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 9
- 标签
- (无)
- 递交数
- 19
- 已通过
- 3
- 上传者