7006. 2021.03.11【2021省赛模拟】玩游戏

\(n\)个人站在一排,每个人抽到一张卡。这些卡是个\(1\)\(n\)的排列。

一开始每个人都不知道自己和别人的卡。

从前往后,每个人可以选择:

  1. 翻开自己的卡,并且公布这张卡的数字。
  2. 和前面在场的人的手上的牌交换并离场,那个人翻开那张卡并公布那张卡的数字。

每个人都绝顶聪明,都希望自己最后得到的牌期望最大。

卡的分配是随机的。问期望下最终留下的人数。

\(k\le 50\)

\(n\le 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}^n\frac{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)+\binom{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;
}
posted @ 2021-03-14 13:40  jz_597  阅读(75)  评论(0编辑  收藏  举报