U179915 关于分级火箭的一点理想化的计算

题目地址

本题是一道疯狂推式子的玄学复杂度sb题。

解题思路

1.数学部分

​ 首先假定已经将火箭分成了 n+1 级,记使用了 n 个分级器。记各级的开始时间点为:

0=t0<t1<t2<<tn<tn+1=T

其中 t0 总的开始,tn+1 为总的结束。称时间段 [tk,tk+1] 为第 k 阶段, 0kn

​ 为了方便,记第 k 阶段除燃料之外的质量总共为:

M×(k)=M0+n+1kn+1M1+(nk)M2

设火箭该阶段燃料质量关于时间的函数为 mk(t) ,根据“火箭瞬时的燃料消耗速度与火箭的瞬时总质量成正比”,以及每一阶段结束时剩余的燃料质量,列出方程:

{dmkdt=1η(mk+M×(k))mk(tk+1)=nkn+1M

解得:

mk(t)=(M×(k)+nkn+1M)etk+1tηM×(k)

​ 考虑第 k 阶段开头,有:

mk(tk)=n+1kn+1M

带入化简可得:

tk+1tkη=ln(M×(k)+n+1kn+1M)ln(M×(k)+nkn+1M)

累加,得到:

Tη=tn+1t0η=k=0ntk+1tkη=k=0n(ln(M×(k)+n+1kn+1M)ln(M×(k)+nkn+1M))

2.代码部分

​ 根据以上推导,我们列出了关于未知数 M 的方程:

k=0n(ln(M×(k)+n+1kn+1M)ln(M×(k)+nkn+1M))=Tη

由于题目仅要求整数级别的复杂度,我们可以通过二分(通过“简单“的求导就能证明其单调性)近似求解 M 的值,每次通过循环计算出上式中等号左侧的值,若不小于右侧则合法。其复杂度为 nlogM

​ 然后我们需要对 n 进行遍历。若采取直接遍历的方法,总复杂度为 n2logM ,会TLE。注意到 M 关于 n 的函数先减后增,于是可以采用爬山算法,随机地进行求解。

代码(数据就是它造的)

#include<iostream>
#include<cmath>
using namespace std;
typedef unsigned long long ull;
int M0, M1, M2, T, eta;		//五个参数
const int steps = 5, step[steps] = { 10000,1000,100,10,1 }, maxn = 100000;
		//步数大小和最大层数,这会影响算法正确性,请“适当”地自行选择
constexpr double Mx(int n, int k) {
	return (double)(n + 1 - k) * M1 / (n + 1) + M0 + (double)(n - k) * M2;
}	//第k阶段除燃料之外的质量
bool check(int n, ull m) {
	double sum = 0;
	for (int k = 0; k <= n; k++)
		sum += log(Mx(n, k) + (double)(n + 1 - k) / (n + 1) * m)
		- log(Mx(n, k) + (double)(n - k) / (n + 1) * m);
	if (sum < (double)T / eta) return false;
	return true;
}	//检测合法性
int main() {
	cin >> M1 >> M2 >> eta >> M0 >> T;
	int ansn = 0;
	ull ansm = (ull)ceil((exp((double)T / eta) - 1) * ((double)M0 + M1));
	for (int i = 0; i < steps; i++) {		//枚举步数大小
		for (int n = ansn + step[i]; n < maxn; n += step[i]) {
			ull lm = (ull)floor(Mx(n, 0) * eta), rm = ansm;
			if (lm > rm || !check(n, rm)) break;
			while (lm < rm) {
				ull mid = (lm + rm) / 2;
				if (check(n, mid)) rm = mid;
				else lm = mid + 1;
			}		//易懂的二分答案
			if (rm < ansm) {		//为使n尽量小,正向时是小于
				ansm = rm; ansn = n;
			}
		}		//正向爬山
		for (int n = ansn - step[i]; n < maxn; n -= step[i]) {
			ull lm = (ull)floor(Mx(n, 0) * eta), rm = ansm;
			if (lm > rm || !check(n, rm)) break;
			while (lm < rm) {
				ull mid = (lm + rm) / 2;
				if (check(n, mid)) rm = mid;
				else lm = mid + 1;
			}
			if (rm <= ansm) {		//为使n尽量小,反向时是小于等于
				ansm = rm; ansn = n;
			}
		}		//反向爬山
	}
	cout << ansn << endl;
	return 0;
}
posted @   Square_Circle  阅读(135)  评论(0编辑  收藏  举报
编辑推荐:
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示