未了 \operatorname{未了} 未了
题目链接: luogu P6473 \operatorname{luogu\ P6473} luogu P6473
题目
由于触犯天神,Sisyphus 将要接受惩罚。
宙斯命 Sisyphus 推一块巨石上长度为 LL 的山坡。Sisyphus 匀速向上推的速度为每年 v v v 的长度(由于是匀速,故经过 1 2 \frac{1}{2} 21 年将能向上推 v 2 \frac{v}{2} 2v 的长度)。然而,宙斯并不希望 Sisyphus 太快到达山顶。宙斯可以施展 n n n 个魔法,若宙斯施展第 i i i 个魔法 ( 1 ≤ i ≤ n ) (1\leq i \leq n) (1≤i≤n) ,则当 Sisyphus 第一次到达位置 a i a_i ai 时,他将会同巨石一起滚落下山底,并从头推起。(滚落的时间忽略不计,即可看作第一次到达位置 a i a_i ai 后 Sisyphus 立即从山底重新出发)
例如宙斯施用了 a i = 3 a_i=3 ai=3 和 a i = 5 a_i=5 ai=5 的两个魔法。Sisyphus 的速度 v = 1 v=1 v=1 ,山坡的长度 L = 6 L = 6 L=6 ,则他推石上山过程如下:
用 3 3 3 年走到位置 3 3 3。
受 a i = 3 a_i=3 ai=3 的魔法影响,回到了山底出发。
再用 3 3 3 年走到位置 3 3 3 ,然而因为是第二次到达, a i = 3 a_i=3 ai=3 的魔法不起作用。
用 2 2 2 年走到位置 5 5 5 。
受 a i = 5 a_i=5 ai=5 的魔法影响,回到了山底出发。
用 6 6 6 年从山底走到了山顶。花费的总时间为 14 14 14 年。
现在,宙斯有 q q q 个询问。对于第 i i i 个询问 t i t_i ti ,宙斯想知道,他最少需要施展多少个魔法才能使 Sisyphus 到达山顶所用的年数大于 t i t_i ti
输入
第一行三个整数 n , L , v n,L,v n,L,v 分别表示魔法的种类数,山坡的长度,Sisyphus 的速度。
第二行 n n n 个整数。第 i i i 个整数 a i a_i ai 表示第 i i i 个魔法作用的位置。 ( 1 < i < n ) (1<i<n) (1<i<n)
第三行一个整数 q q q 表示宙斯的询问个数。
接下来 q q q 行每行一个整数,第 i i i 行的整数 t i t_i ti 表示宙斯的第 i i i 个询问。 ( 1 < i < n ) (1<i<n) (1<i<n)
输出
输出 q q q 行,每行恰好一个整数,第 i i i 行的整数对应第 i i i 个询问的答案。 ( 1 ≤ i ≤ q ) (1 \leq i\leq q) (1≤i≤q)
如果宙斯无论如何都不能使 Sisyphus 使用的年数大于 t i t_i ti ,请输出 − 1 -1 −1 。
样例输入
3 6 3
3 5 1
4
1
3
4
5
样例输出
0
1
2
-1
样例解释
- 不使用任何魔法,Sisyphus 需要 2 2 2 年走上山顶。
- 使用魔法 2 2 2 ,Sisyphus 需要 11 3 \frac{11}{3} 311 年走上山顶。(用时 5 3 \frac{5}{3} 35 年走到魔法 2 2 2 的位置并滚落下山,再用时 6 3 = 2 \frac{6}{3}=2 36=2 年走到山顶)
- 使用魔法 1 , 2 1,2 1,2 ,Sisyphus 需要 14 3 \frac{14}{3} 314 年走上山顶。
- 宙斯不能使 Sisyphus 用大于 5 5 5 年的时间走上山顶。
数据范围
对于测试点 1 ∼ 8 : n = 1 1\sim 8:n=1 1∼8:n=1 。
对于测试点 9 ∼ 12 : n = 2 9\sim 12:n=2 9∼12:n=2 。
对于测试点 13 ∼ 17 : n , q ≤ 1000 13\sim 17:n,q\le 1000 13∼17:n,q≤1000 。
对于所有测试点: 1 ≤ n , q ≤ 2 × 1 0 5 1 \leq n,q \leq 2 \times 10^5 1≤n,q≤2×105 , 1 ≤ v ≤ L ≤ 1 0 9 1\leq v\leq L\leq 10^{9} 1≤v≤L≤109 , 1 ≤ a i < L 1\leq a_i < L 1≤ai<L , 1 ≤ t i ≤ 1 0 9 1 \leq t_i\leq 10^9 1≤ti≤109 。
数据保证 a i a_i ai 两两不同。
思路
这道题是一道二分。
我们可以很清楚的看出肯定是从位置高的选起。(这样才能耗时间)
那我们把所有魔法高度从大到小排序,然后前缀和,我们就可以二分出答案了。
代码
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
ll n, L, v, a[200001], T, x, sum, left, right;
bool cmp(ll x, ll y) {
return x > y;
}
int main() {
scanf("%lld %lld %lld", &n, &L, &v);//读入
for (ll i = 1; i <= n; i++)
scanf("%lld", &a[i]);//读入
sort(a + 1, a + n + 1, cmp);//排序
for (ll i = 2; i <= n; i++)
a[i] += a[i - 1];//前缀和
scanf("%lld", &T);//读入
for (ll saa = 1; saa <= T; saa++) {
scanf("%lld", &x);//读入
sum = x * v;//要走的距离
if (sum >= L + a[n]) {//无论怎么放都不行
printf("-1\n");
continue;
}
left = 0;
right = n;
while (left < right) {//二分
ll mid = (left + right) / 2;
if (a[mid] + L < sum) left = mid + 1;
else right = mid;
}
printf("%lld\n", left);//输出
}
return 0;
}