1 条题解
-
0
题目分析
本题要求从一个未知的单调递增序列中,选择 个数,使得它们的和在 范围内。我们最多只能查询 个元素的值。这是一个典型的交互式选择问题,需要在有限的查询次数内找到满足条件的解。
关键观察
-
序列的单调性: 序列 是单调递增的,即 。这一性质是关键,允许我们进行二分搜索和范围推断。
-
条件限制:
需要选择恰好 个数
和必须在 范围内
最多查询 个元素的值
- 贪心策略:
最小的可能和:选择前 个数
最大的可能和:选择后 个数
如果最小和已经大于 或最大和小于 ,则无解
算法思路
- 初步检查与处理
算法首先查询前 个元素:
如果这 个数的和已经在 范围内,直接返回答案
如果第 个元素 ,那么最小的 个元素之和至少为 ,当 时可能超过 ,需要进一步检查
- 二分搜索关键位置
通过二分搜索找到第一个大于 的元素位置:
int l = K + 1, r = N + 1; while (l < r) { int mid = l + r >> 1; if (skim(mid) > A) r = mid; else l = mid + 1; }这个位置记为 ,满足 且 (如果存在)。
- 替换策略
情况 1: 用第 个元素替换第 个元素
新的和为:
由于 ,而前 个元素的和较小,总和可能在 内
检查条件:如果 ,则成功
情况 2: 滑动窗口替换
如果情况 1 不满足,尝试用后面的元素替换前面的多个元素:
从位置 开始,向前尝试最多 个位置
每次用 替换掉当前集合中最小的元素
随着替换的进行,总和逐渐增大
当总和首次达到 时,检查是否
正确性证明
- 单调性的利用:
序列单调递增,因此替换较大元素会增大总和
二分搜索正确找到第一个大于 的元素
- 替换策略的正确性:
情况 1:用一个大元素 替换 ,可能使总和进入目标区间
情况 2:逐步替换,总和单调递增。由于序列递增,每次替换都会增加总和
- 边界情况处理:
如果 且前 个元素和超过 ,则可能无解
如果所有元素都太小,无法达到 ,则无解
- 查询次数控制:
最多查询 (前 个)+ 1(二分搜索)+ (滑动窗口)= 次
由于 ,最多 21 次查询,远小于 ()
时间复杂度
-
查询次数:,最多约 次
-
计算复杂度:
-
总复杂度:,非常高效
AC代码
#include <bits/stdc++.h> #include "books.h" #define pb push_back using namespace std; typedef long long ll; typedef vector<int> vec; const int N = 1e5 + 10; ll x[N], sum; void solve(int N, int K, ll A, int S) { vec ans; for (int i = 1; i <= K; i ++) { sum += (x[i] = skim(i)); ans.pb(i); } if (sum >= A && sum <= A * 2) answer(ans); if (x[K] >= A) impossible(); int l = K + 1, r = N + 1; while (l < r) { int mid = l + r >> 1; if (skim(mid) > A) r = mid; else l = mid + 1; } if (r <= N && sum - x[K] + skim(r) <= 2 * A) { ans.pop_back(); ans.pb(r); answer(ans); } for (int i = l - 1; i >= max(l - K, K + 1); i --) { sum += skim(i) - x[l - i]; ans.erase(ans.begin()), ans.pb(i); if (sum >= A) answer(ans); } impossible(); } -
- 1
信息
- ID
- 6125
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 5
- 已通过
- 1
- 上传者