jzoj1897. 书堆(调和级数公式)

题目描述

Description
  蚂蚁是勤劳的动物,他们喜欢挑战极限。现在他们迎来了一个难题!蚂蚁居住在图书馆里,图书馆里有大量的书籍。书是形状大小质量都一样的矩形。蚂蚁要把这些书摆在水平桌子的边缘。蚂蚁喜欢整洁的布置,所以蚂蚁规定书本必须水平摆放,宽必须平行于桌缘(如图),而且不允许同一高度摆多本书
  在这里插入图片描述
  蚂蚁想要让书本伸出桌子边缘尽量远,同时不让书因为重力垮下来。它们已经用不知道什么方法测出了书的长度M(如图)。如果总共有N本书,请你帮忙计算如何摆放使得最多水平伸出桌缘多远。你不用考虑蚂蚁用什么方法搭建这堆书。
  如果某本书以上的所有书的重心的竖直射影不在这本书上,或者正好落在在这本书的边界上,那么这堆书是不稳定的,会因为重力而垮下来。
  ()不考虑地球自转,重力系数也不因高度改变;
  (
)书是质量均匀,质地坚硬的理想二维物体;
  (*)在不会垮的前提下,每本书的位置坐标可以是任意实数。

Input
  输入文件仅含一行,两个正整数N和M,表示书本数和书本长度。

Output
  输出仅包含一行,整数L,表示水平延伸最远的整数距离 (不大于答案的最大整数,详见样例)

Sample Input
【输入样例一】
1 100

【输出样例一】
49

【输入样例二】
2 100

【输出样例二】
74

Sample Output

Data Constraint

Hint
【数据范围】
  10%的数据中N≤5;
  20%的数据中N≤10^3;
  40%的数据中N≤10^7;
  100%的数据中N≤1018;答案≤106。

20~40%(?)

从上往下设其伸出长度为ai
二分a1,利用重心的约束条件来往下求
最后判断(a1+…+an)/n<m/2

code

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define abs(x) ((x)>0?(x):-(x))
using namespace std;

int l,r,mid;
long long n,m;
long double sum,M;
bool bz;

bool pd(double a)
{
	int i,j,k,l;
	
	sum=a;
	
	fo(i,2,n)
	{
		a=sum/(i-1)-M;
		sum+=a;
	}
	
	sum/=n;
	
	if (abs(sum-M)<=0.00000001)
	return 0;
	
	if (sum<M)
	return 1;
	else
	return 0;
}

int main()
{
//	freopen("S8_2_1.in","r",stdin);
	
	scanf("%lld%lld",&n,&m);
	M=m/2.0;
	
	if (n<=1000000)
	{
		l=1;
		r=1000000;
	}
	else
	{
		l=7*m;
		r=9*m;
	}
	
	while (l<r)
	{
		mid=(l+r)/2;
		
		if (pd(mid))
		l=mid+1;
		else
		r=mid;
	}
	
	if (!pd(l))
	--l;
	
	printf("%d\n",l);
}

40%

假设把书堆反过来,变成从左往右(从上往下)放
第一本书的左边界为x=0,那么最终答案=所有书的重心
设S表示已经放了的i-1本书的重心和,G表示i-1本书的重心
显然G=S/(i-1)

考虑贪心放第i本书
显然i的左边界最多能贴着G,那么i的重心为G+M/2
则S+=G+M/2,S=S+S/(i-1)+M/2(i≥2)

最终答案为G

100%

化一下式子
S 1 = M 2 S_1=\frac{M}{2} S1=2M
S = S + S i − 1 + M 2 S=S+\frac{S}{i-1}+\frac{M}{2} S=S+i1S+2M(i≥2)
= S ∗ i i − 1 + M 2 =S*\frac{i}{i-1}+\frac{M}{2} =Si1i+2M
可以发现最终的S为
S = M 2 ∗ ∑ i = 1 n n i S=\frac{M}{2}*\sum_{i=1}^{n}{\frac{n}{i}} S=2Mi=1nin
G = S n = M 2 ∗ ∑ i = 1 n 1 i G=\frac{S}{n}=\frac{M}{2}*\sum_{i=1}^{n}{\frac{1}{i}} G=nS=2Mi=1ni1


当n很大(>107)时,后面的可以用调和级数公式来算
调和级数公式(n趋近于无穷):
∑ i = 1 n 1 i ≈ ln ⁡ ( n ) + γ \sum_{i=1}^{n}{\frac{1}{i}} \approx \ln(n)+\gamma i=1ni1ln(n)+γ,其中 γ \gamma γ欧拉常数,约等于0.5772156649
一些定义:
https://baike.baidu.com/item/欧拉常数/5371177?fr=aladdin
在这里插入图片描述
(这个貌似可以手算机算)
https://en.wikipedia.org/wiki/Natural_logarithm
在这里插入图片描述
∑ i = 1 n 1 i = ∫ 1 n + 1 1 ⌊ x ⌋ d x \sum_{i=1}^{n}{\frac{1}{i}}=\int_{1}^{n+1}{\frac{1}{\left \lfloor x \right \rfloor}}dx i=1ni1=1n+1x1dx

推导

∑ i = 1 n 1 i = ∫ 1 n + 1 1 ⌊ x ⌋ d x \sum_{i=1}^{n}{\frac{1}{i}}=\int_{1}^{n+1}{\frac{1}{\left \lfloor x \right \rfloor}}dx i=1ni1=1n+1x1dx
= ∫ 1 n + 1 1 x d x + ∫ 1 n + 1 ( 1 ⌊ x ⌋ − 1 x ) d x =\int_{1}^{n+1}{\frac{1}{x}}dx+\int_{1}^{n+1}{(\frac{1}{\left \lfloor x \right \rfloor}-\frac{1}{x})}dx =1n+1x1dx+1n+1(x1x1)dx
= ln ⁡ ( n + 1 ) + ∫ 1 n + 1 ( 1 ⌊ x ⌋ − 1 x ) d x =\ln(n+1)+\int_{1}^{n+1}{(\frac{1}{\left \lfloor x \right \rfloor}-\frac{1}{x})}dx =ln(n+1)+1n+1(x1x1)dx
其实当n大时ln(n)和ln(n+1)差不多
≈ ln ⁡ ( n ) + γ \approx \ln(n)+\gamma ln(n)+γ(n+1≈∞)

code

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define r 0.577215664901532860606512090082402431042159335
using namespace std;

int i,j,k,l;
long long n;
long double S,m;

int main()
{
//	freopen("S8_2_1.in","r",stdin);
	
	scanf("%lld%Lf",&n,&m);
	m/=2.0;
	
//	S+=G+M/2 G=S/i
	
//	40%
//	S=m;
//	fo(i,2,n)
//	S+=S/(i-1)+m;
//	S/=n;
	
//	100%
	if (n<=10000000)
	{
		fo(i,1,n)
		S+=1.0/i;
		S*=m;
	}
	else
	S=m*(log(n)+r);
	
	printf("%0.0Lf\n",floor(S-0.0000001));
}

附·欧拉常数近似计算

在这里插入图片描述
根据积分的定义:[a,b]段曲线与x轴之间的面积,化成若干矩形来计算面积和

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define E 0.0001
using namespace std;

long double euler,i;

int main()
{
	i=1;
	
	while (i<=10000)
	{
		euler+=(1.0/floor(i)-1.0/i);
		i+=E;
	}
	
	printf("%0.10Lf\n",euler*E);
}

算得γ=0.5771351607,足以通过本题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值