洛谷 P4820 [国家集训队]书堆
题目描述
让N本密度均匀的相同的书摆在桌上。最下面的摆在桌子边缘,其他的书依次堆叠在一本书上。每本书都只能水平放置,宽必须垂直于桌子边缘。如(洛谷的)图:
求书本最右能到多远(这个距离是指最右边的书的右端点-桌子的右边缘),同时不让书因为重力垮下来。每本书的长度为M,最大化L。
当然是在“理想状态”下。即不考虑地球自转,重力系数改变,书的形变。
但是如果某本书以上的所有书的重心的竖直射影不在这本书上,或者正好落在在这本书的边界上,那么这堆书是不稳定的,会因为重力而垮下来。
输出延伸最远的距离向下取整。
\(N\le 10^{18}\),保证答案\(<10^6\)。
题意分析
这道题前不久我的物理老师\(\times5\)大人给我们讲过!
首先确定递推的方向应该是从上往下,否则上面的会影响下面的书的重心。
(以下默认长度为1)
n=1,\(\frac12\),下面的书的右端点对齐上面的书的重心。
n=2,\(\frac12+\frac14=\frac34\),第三本书对齐前两本书的重心。
n=3,\(\frac12+\frac14+\frac16=\frac{11}{12}\),第四本书对齐前三本书的重心距离。
\(\dots\)
然后就发现答案是
注意以上考虑的是重心正好落在了下一本书的边缘上。而题目中说了,正好落在边界上是不稳定的!所以要减去一个极小的数,当作是那个\(\varepsilon\)就好。
但是这不可能通过\(N\le10^{18}\)的数据的。
如果您在这个地方发现有的点是WA的话,请检查您有没有开\(\texttt{long long}\)哦~
for(LL i=1;i<=n;i++) ans+=0.5/i;
cout<<(LL)(ans*m-(1e-6));
这样大概可以做到\(10^9\),您就可以获得\(36pts\)的好成绩!
考虑在数学方面搞点文章。
可以发现这个东西就是调和级数\(H(x)\div2\)而来的
又因为\(H(x)\)和\(\ln(x)\)特别特别的相近(\(\lim_{x\to\infty}\))
第n个调和数与n的自然对数自然对数的差值收敛于欧拉常数。
所以对于很大的\(n\)我们只要计算\(\ln(n)+0.5772156649\dots\)即可。
Code
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<queue>
#include<vector>
#define IL inline
#define re register
#define LL long long
#define ULL unsigned long long
#define debug printf("Now is %d\n",__LINE__);
using namespace std;
inline LL read()
{
LL x=0,fu=1;
char ch=getchar();
while(!isdigit(ch)&&ch!='-') ch=getchar();
if(ch=='-') fu=-1,ch=getchar();
x=ch-'0';ch=getchar();
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return x*fu;
}
double ans;
LL n,m;
int main()
{
n=read();
m=read();
if(n>100000000) cout<<(LL)(m*(log(n)+0.5772156649)/2-(1e-6))<<endl;
else
{
for(LL i=1;i<=n;i++) ans+=0.5/i;
cout<<(LL)(ans*m-(1e-6))<<endl;
}
return 0;
}