[BZOJ 2048] [2009国家集训队]书堆 【调和级数】

题目链接:BZOJ - 2048

 

题目分析

只有一本书时,这本书的重心落在桌子边缘上,伸出桌面的长度就是 1/2。

有两本书时,第一本书的重心就落在第二本书的边缘上,两本书的重心落在桌子边缘上,两本书的重心就是在最下面一本书的右端 1/4  处。那么伸出 1/2 + 1/4 。

三本书时,可以再多伸长 3 本书的重心 1/6 。

继续计算可以发现规律,i 本书的重心就落在最下面一本书的右端 1/2i 处。

那么我们要求的伸出的总长度就是 sigma(1 / 2i) = sigma(1 / i) / 2。

sigma(1 / 2i) 就是调和级数求和,如果 n 比较小,我们就直接 O(n) 求,因为 n 比较小的时候用极限公式求误差会比较大。

如果 n 很大,我们就用调和级数的极限公式 sigma(1 / i) (i = 1 to n) = ln(n + 1) + r。r 是欧拉常数,r = 0.5772156649015328...

然后就做完了。

 

代码

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>

using namespace std;

typedef double LF;
typedef long long LL;

#define r 0.5772156649

const LF Eps = 1e-10;

LL n, m;

LF Ans;

int main()
{
	scanf("%lld%lld", &n, &m);
	if (n <= 1000000ll)
	{	
		for (int i = 1; i <= n; ++i)
			Ans += 1.0 / (LF)i;
	}
	else Ans = log((LF)(n + 1)) + r;
	Ans /= 2.0; Ans *= (LF)m;
	printf("%d\n", (int)(Ans - Eps));
	return 0;
}

  

posted @ 2015-06-10 15:14  JoeFan  阅读(395)  评论(0编辑  收藏  举报