题面
题意: 主人公在一堆人里选人,他们的能力值分别为 1 − n 1-n 1−n,主人公采取如下策略:取一个 k k k,这 k k k个人全不要,设这 k k k个人中能力值最大的为 m m m,从 k + 1 k+1 k+1开始选出第一个比 m m m大的。要让选出的人是能力值最高的概率最大,求 m m m。
考试时做到这题已经没时间耐心推导规律了,就打了几个表去 o e i s oeis oeis上找规律,发现了一个神奇规律 ⌊ n n + 1 ( n + 1 ) n ⌋ \lfloor\frac{n^{n+1}}{(n+1)^n}\rfloor ⌊(n+1)nnn+1⌋,正好可以过样例,调调改改直接交了,结果 W A WA WA了,后来对拍发现差了好多。
其实公式并不难推,我们设 f ( n , k ) f(n,k) f(n,k)为取到能力值最高的概率,则有
f ( n , 0 ) = 1 n , f(n,0)=\frac{1}{n}, f(n,0)=n1,
f ( n , k ) = ∑ i = k + 1 n 1 n k i − 1 = k n ∑ i = k n − 1 1 i f(n,k)=\sum_{i=k+1}^{n}\frac{1}{n}\frac{k}{i-1}=\frac{k}{n}\sum_{i=k}^{n-1}\frac{1}{i} f(n,k)=∑i=k+1nn1i−1k=nk∑i=kn−1i1。
因此求出 1 i \frac{1}{i} i1的前缀和,遍历枚举 k k k即可。时间复杂度 O ( T n ) O(Tn) O(Tn)
如果再对这个函数进行求导,可以发现这个函数是先增后减的,最大值为 n e \frac{n}{e} en,因此求出 n e \frac{n}{e} en,并枚举左右两侧的数也可以 O ( T ) O(T) O(T)求出结果。
#include
#define N 10010
using namespace std;
double sum[N],ans;
inline void read(int &x){
int s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){s=(s<<3)+(s<<1)+(ch&15);ch=getchar();}
x=s*w;
}
int n,T;
int main ()
{
for(int i=1;i<=10000;i++)sum[i]=sum[i-1]+1.0/i;
read(T);
while(T--){
read(n);double ans=1.0/n;int ak=0;
for(int k=1;k<=n;k++){
if(k*1.0/n*(sum[n-1]-sum[k-1])>ans)ans=k*1.0/n*(sum[n-1]-sum[k-1]),ak=k;
}
cout<<ak<<endl;
}
}