有(n)个人站在一排,每个人抽到一张卡。这些卡是个(1)到(n)的排列。
一开始每个人都不知道自己和别人的卡。
从前往后,每个人可以选择:
- 翻开自己的卡,并且公布这张卡的数字。
- 和前面在场的人的手上的牌交换并离场,那个人翻开那张卡并公布那张卡的数字。
每个人都绝顶聪明,都希望自己最后得到的牌期望最大。
卡的分配是随机的。问期望下最终留下的人数。
(kle 50)
(nle 10^5)
输出实数。
(P)表示前面留下来的数字的集合,(R)表示后面的数字的集合。
结论:如果(maxP< minR),则不换;否则换。
证明:(maxP< minR)时不换显然,以下证(maxP>minR)时换。
如果换,收益为(maxP)。如果不换,归纳证明:
称关键时刻为下次(P)中排位变化,或出现(maxP'<minR’)。
以下证明:对于当前的(maxP),设它为(v_x),到下次关键时刻的(v_x'),一定有(v_x'<v_x)。
在关键时刻之前,一定都是选择换。如果是(P)的排位变化,那么此时抽到的值一定小于(P)的次大值。于是(maxP')变成了(P)的次大值。此时显然(v_x'<v_x)。
如果是出现(maxP'<minR'),排位没有变,(v_x)一直是(maxP)。则如果(minR)不变,那么(maxP>minR)变化到(maxP'<minR'),(maxP)是变小了;如果(minR)变了,考虑从前推过来,某一刻当前(maxP)用来和当前(minR)交换,此时满足(maxP>minR),则(maxP)变小,然后继续往后推。所以也有(v_x'<v_x)
综上,到了关键时刻,一定有(v_x'<v_x)。
用DYP的话来说,就是:
以某个时刻的(v_x)标准,它可能会换到(v_x)以上,因为后面还会继续换所以不管它。于是它肯定要换小。
在(minR)不改变的时候,无论怎么换都会保持(maxP>minR),因为换来的东西都比(minR)大。
换小后如果产生了一些显著变化,它可能不是最大了,也有可能把(minR)换了下来。(即“关键时刻”)
接下来证明:如果在(maxP>minR)时不换,没可能得到比(maxP)更大的收益。设当前点为(v_y)。
首先下一个关键时刻总是存在的。最后一次一定是关键时刻。
如果开始时(v_y<maxP),如果(v_y)能得到更大的收益,那么(P)中大于(v_y)的一定的排位一定要换到(v_y)后面(指值变得更小),使(v_y)露出来。从(v_y)露出来那个时候开始记,根据上面的结论,到达下一个关键时刻的时候,(v_y)一定会变小,并且可能换到后面去。重复刚刚的分析,最终(v_y)不会变大。
如果开始时(v_y>maxP),则到下一次关键时刻的时候,(v_y)可能会换到后面去,可能会换掉(minR)但由于之前(minP>minR)所以一定会换到后面去。然后就和上面一样了。
证毕。
模拟可以验证最终保留在(P)中的元素一定都是在原序列中的后缀最小值。
于是(S_0(n)=sum_{i=1}^nfrac{1}{n}=H(n))(调和级数)
设(S)的生成函数为(F)。(F_k(x)=frac{-ln(1-x)}{(1-x)^{k+1}})。
求导得((F_k(x))'=frac{1}{(1-x)^{k+2}}+(k+1)F_{k+1}(x))
由于(F_k(x)[x^n]=frac{1}{n}(F_{k}(x))'[x^{n-1}]),得到(nS_k(n)=(k+1)S_{k+1}(n-1)+inom{n+k}{k+1})。
于是可以从(S_{k}(n+1))推到(S_{k+1}(n))。
最终算(H(n))的时候,(n)小的时候暴力算,(n)大的时候根据常识有(H(n))约等于(ln(n)+gamma+frac{1}{2n}),其中(gamma)为欧拉常数$$。
题解中也提到(S_k(n))约等于(frac{n^k}{k!}(H(n)-H(k)))。不知道是什么东西。
using namespace std;
#include <bits/stdc++.h>
#define ll long long
long double C(ll m,ll n){
long double p=1;
for (ll i=m;i>m-n;--i) p*=i;
for (ll i=1;i<=n;++i) p/=i;
return p;
}
long double S(ll k,ll n){
if (k==0){
long double s=0;
if (n<=1e7)
for (int i=1;i<=n;++i)
s+=1.0/i;
else
s=log(n)+0.5772156649+1.0/(2*n);
return s;
}
return ((n+1)*S(k-1,n+1)-C(n+k,k))/k;
}
int main(){
// freopen("in.txt","r",stdin);
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
cout.unsetf(ios::fixed);
cout.setf(ios::scientific);
cout.precision(9);
ll k,n;
cin>>k>>n;
cout<<S(k,n);
return 0;
}