XJTUOJ #1030 JM的星系战争

题目描述:

今天,带恶人JM掀起了一场超大规模的星系战争,为了阻止疯狂的JM,你需要前往n个星球去招募勇士(为了部落!)。这些星球的编号为1-n,地球(也就是你的初始所在)为1号星球。为了掩人耳目(?)你必须按照编号顺序依次访问这些星球,然后再回到地球上,也就是按照1\(\rightarrow\)2\(\rightarrow\)···\(\rightarrow\)n\(\rightarrow\)1的顺序访问。

当你驾驶飞船访问每个星球时,你的飞船都需要在星球上着陆然后再起飞,这两个过程都是需要耗费大量的燃料的。因为这是一艘神(zhi)奇(zhang)的飞船,它只在着陆和起飞时有大量消耗,至于在星球之间飞行所耗费的燃料,少到可以忽略不计。

对于编号为 \(i\) 的星球,着陆所需的燃料为\(M_1\)/\(a_i\),起飞所需的燃料为\(M_2\)/\(b_i\),其中\(M_1\)\(M_2\)分别表示着陆或起飞之前飞船的总重。飞船的总重可以表示为\(M=m+f\),其中\(m\)表示飞船的净重,\(f\) 表示飞船上当前搭载的燃料重量。

例如在编号为 \(i\) 的星球上着陆之前有\(m=9,f=3,a_i=8,b_i=2\),那么着陆需要\((9+3)/8=1.5\)吨燃料,而起飞需要\((9+1.5)/2=5.25\)吨燃料,因为着陆以后燃料被消费掉了,起飞之前\(f=1.5\)而不是\(3\)

注意,整个过程是最开始从地球起飞,然后依次在\(2...n\)号星球着陆然后起飞,最后在地球着陆。

因为JM的军队已经将这些星球上的燃料掠夺一空,所以你没法在路上补充燃料,你的燃料只能在出发前全部装好。

同样是因为JM的军队的疯狂掠夺,现在燃料已经十分紧缺,你希望花费最少的燃料来完成招募任务。请计算访问这些星球并返回地球所需的最少燃料。上述所有重量的计数单位均相同。

输入格式:

第一行两个正整数\(n\)\(m\),表示星球个数和飞船净重。

接下来\(n\)行,每行两个正整数\(a_i,b_i\),分别表示每个星球的着陆参数和起飞参数,其作用如题目所述。

输出格式:

输出一行一个浮点数表示答案。但是如果无论装载多少燃料都无法完成招募任务,则输出一个整数\(-1\)不带浮点。

当答案的绝对值误差不超过\(10^{-4}\)时即会被评测通过。

保证如果有解,则解小于\(10^{18}\)

思路:

首先,一个很明显的思路是可以二分答案,模拟验证是否可行,但是\(n\)很大,有超时风险。

注意到燃料要最少,所以在地球着陆后剩的燃料为\(0\),这就是我们的末状态。

那么可以想到倒推,既然知道末状态,那么末状态之前一步是什么呢?列方程:\((m+\Delta)/a_1=\Delta\)(\(\Delta\)是着陆消耗的燃料)。

移项得:\(\Delta=m/(a_1-1)\)

同理,若着陆或起飞后燃料为\(ans\),那么之前燃料的值为\((m+a_i*ans)/(a_i-1)\)\((m+b_i*ans)/(b_i-1)\)

代码如下:

#include<cstdio>
#include<cstdlib>
#define db double
#define maxn (int)(2*1e5)+1
db a[maxn],b[maxn];
int main(){
	int i,j,n,x,y;
	db ans=0,m;
	scanf("%d%lf",&n,&m);
	for(i=1;i<=n;i++){
		scanf("%d%d",&x,&y);
		if(x<=1||y<=1){
			printf("-1");
			return 0;
		}
		a[i]=x;b[i]=y;
	}
	ans=m/(a[1]-1);
	for(i=n;i>=2;i--){
		ans=(m+b[i]*ans)/(b[i]-1);
		ans=(m+a[i]*ans)/(a[i]-1);
	}
	ans=(m+b[1]*ans)/(b[1]-1);
	printf("%.6lf",ans);
	return 0;
}
posted @ 2020-09-27 15:41  文艺平衡树  阅读(140)  评论(0编辑  收藏  举报