欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【二分】 [NOI Online #2 入门组]未了

程序员文章站 2022-03-13 22:54:14
...

LinkLink

luogu P6473luogu\ P6473

DescriptionDescription

由于触犯天神,SisyphusSisyphus 将要接受惩罚。
宙斯命 SisyphusSisyphus 推一块巨石上长度为 LL 的山坡。SisyphusSisyphus 匀速向上推的速度为每年 vv 的长度(由于是匀速,故经过 12\frac{1}{2} 年将能向上推 v2\frac{v}{2}

的长度)。然而,宙斯并不希望SisyphusSisyphus太快到达山顶。宙斯可以施展 nn 个魔法,若宙斯施展第 ii 个魔法 (1in1\leq i \leq n) ,则当 SisyphusSisyphus 第一次到达位置 aia_i

时,他将会同巨石一起滚落下山底,并从头推起。(滚落的时间忽略不计,即可看作第一次到达位置 aia_iSisyphusSisyphus 立即从山底重新出发)

例如宙斯施用了 ai=3a_i=3ai=5a_i=5

=5 的两个魔法。SisyphusSisyphus 的速度 v=1v=1 ,山坡的长度 L=6L = 6,则他推石上山过程如下:

33 年走到位置 33

ai=3a_i=3 的魔法影响,回到了山底出发。

再用 33 年走到位置 33,然而因为是第二次到达,ai=3a_i=3 的魔法不起作用。

22 年走到位置 55

ai=5a_i=5 的魔法影响,回到了山底出发。

66 年从山底走到了山顶。花费的总时间为 1414 年。

现在,宙斯有 qq 个询问。对于第 ii 个询问 tit_i,宙斯想知道,他最少需要施展多少个魔法才能使 SisyphusSisyphus 到达山顶所用的年数大于 tit_i

InputInput

第一行三个整数 n,L,vn,L,v 分别表示魔法的种类数,山坡的长度,SisyphusSisyphus 的速度。

第二行 nn 个整数。第 ii 个整数 aia_i​, 表示第 ii 个魔法作用的位置。(1<i<n)(1<i<n)
第三行一个整数 qq 表示宙斯的询问个数。

接下来 qq 行每行一个整数,第 ii 行的整数 tit_i​, 表示宙斯的第 ii 个询问。

OutputOutput

输出 qq 行,每行恰好一个整数,第 ii 行的整数对应第 ii 个询问的答案。(1iq)(1 \leq i\leq q)

如果宙斯无论如何都不能使 SisyphusSisyphus 使用的年数大于 tit_i,请输出 1-1

SampleSample InputInput

3 6 3
3 5 1
4
1
3
4
5

SampleSample OutputOutput

0
1
2
-1

HintHint

不使用任何魔法,SisyphusSisyphus 需要 22 年走上山顶。
使用魔法 22SisyphusSisyphus 需要 113\frac{11}{3} 年走上山顶。
用时 53\frac{5}{3} 年走到魔法 22 的位置并滚落下山
再用时 63=2\frac{6}{3}=2 年走到山顶
使用魔法 1,21,2SisyphusSisyphus 需要 143\frac{14}{3} 年走上山顶。
宙斯不能使 SisyphusSisyphus 用大于 55 年的时间走上山顶。

TrainTrain ofof ThoughtThought

很容易想到,肯定是越往后的魔法越优,因此将魔法倒序
然后求出前缀和,很容易想到一个个比较,但是这样会炸掉
因此我们要使用二分
求出第一个比读入的时间大的位置(即为答案)
还有一些特殊情况需要特判一下

记得用doubledouble

CodeCode

#include<algorithm>
#include<iostream>
#include<cstdio>

using namespace std;

double l, v, a[200005], s[200005];
long long n;

bool cmp(double i, double j)
{return i > j;}//降序排序

int main()
{
	scanf("%lld%lf%lf", &n, &l, &v);
	s[0] = l / v;
	for (int i = 1; i <= n; ++i)
		scanf("%lf", &a[i]);
	sort(a + 1, a + n + 1, cmp);
	for (int i = 1; i <= n; ++i)
		s[i] = s[i - 1] + double(a[i]) / v;//求前缀和
	int q;
	scanf("%d", &q);
	while(q--)
	{
		int x;
		scanf("%d", &x);
		if (x > s[n]) printf("-1\n");
		else {
			int l = 0, r = n, ans = 0;
			while (l <= r)
			{
				int mid = (l + r) / 2;
				if (s[mid] > x) r = mid - 1, ans = mid;
				else l = mid + 1;
			}//二分模板
			printf("%d\n", ans);
		} 
	}
	return 0;
}
相关标签: 二分 二分