传送门:luoguT39018
题解
时直接输出 的逆元。
这题一看是个
,设
表示在第
轮后,已更换
次的蒙对的最大概率。
转移方程似乎是这样的:
。
先不说取模的问题,光这个方程都是错的(摔)。
考虑这一轮不换,自然是从
转移,但换的情况按上式转移明显是错的,设
表示在
轮后更换
次的蒙对的最小概率,从
转移过来才是最优的。
于是转移方程变成了两个:
这样
有
。
但是模意义下表达的分式无法判断大小,所以还得另存两个
的数组存原值判大小,这边模意义下的就按照这两个原值数组的操作进行处理。
下面说说奇妙的正解。
先考虑
的情况,假设进行了某次操作后还剩下
个选项,蒙对的概率为
,那么若这次操作更换了选项,更换后的概率即为
。
既然
,先假设在第
次更换选项,则需要保证
尽可能小,而
的下界即为
,这个值是唯一确定的,即
。
那么当
时,
中的
达到了最小值
,所以证明了
时,把更改操作放在最后一轮可以得到最大的蒙对概率。
当
时
观察之前列的
式子:
可以发现当进行到第
次操作时,
必然得到的是
的前缀最小值。
那么最后一次更换操作后得到
,
在
时所达到下界
必然是全局最小的,所以
在最后一次操作达到了最大,进而说明最后一次更换选项必然要放在最后一轮。
那么考虑剩下的
次操作,需要使操作后的
尽量小。
考虑连续两次的操作后使初始
,变为
。
同样这里考虑一次更改操作都没有前的
是唯一确定的,即
,所以只需要让
尽量大。而我们可以把前
次操作看做连续的两次操作的叠加,所以把这
次更改分别放在前
轮进行即可使最后得到的
最小。
那么综上,对于任意满足条件的
均成立一下做法得到最大的蒙对概率:
前
轮更换选项,最后一轮更换选项。
代码
的
#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
#define db double
int n,ky,f[1010][1010],g[1010][1010],nv[100050];
db ff[1010][1010],gg[1010][1010];
char c;
inline int dc(int x,int y){x-=y;if(x<0) x+=mod;return x;}
inline int mul(int x,int y){return 1ll*x*y%mod;}
int main(){
int i,j,lim;
scanf("%d%d",&n,&ky);
nv[0]=nv[1]=1;
for(i=2;i<=n;++i) nv[i]=mul(mod-mod/i,nv[mod%i]);
if(ky==0){printf("%d\n",nv[n]);return 0;}
for(i=0;i<=n-2;++i) for(j=0;j<=ky;++j) gg[i][j]=1e12,ff[i][j]=-1e12;
f[0][0]=g[0][0]=nv[n];ff[0][0]=gg[0][0]=1/(db)n;
for(i=1;i<=n-2;++i){
lim=min(i,ky);
f[i][0]=g[i][0]=nv[n];ff[i][0]=gg[i][0]=1/(db)n;
for(j=1;j<=lim;++j){
if(ff[i-1][j]>=(1-gg[i-1][j-1])/(db)(n-i-1)){
f[i][j]=f[i-1][j];
ff[i][j]=ff[i-1][j];
}else{
f[i][j]=mul(dc(1,g[i-1][j-1]),nv[n-i-1]);
ff[i][j]=(1-gg[i-1][j-1])/(db)(n-i-1);
}
if(gg[i-1][j]<=(1-ff[i-1][j-1])/(db)(n-i-1)){
g[i][j]=g[i-1][j];
gg[i][j]=gg[i-1][j];
}else{
g[i][j]=mul(dc(1,f[i-1][j-1]),nv[n-i-1]);
gg[i][j]=(1-ff[i-1][j-1])/(db)(n-i-1);
}
}
}
printf("%d\n",f[n-2][ky]);
return 0;
}
的找规律
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10,mod=1e9+7;
int nv[N],n,ky,ans;
inline int mul(int x,int y){return 1ll*x*y%mod;}
inline int dc(int x,int y){x-=y;if(x<0) x+=mod;return x;}
int main(){
int i,j;
scanf("%d%d",&n,&ky);
nv[0]=nv[1]=1;
for(i=2;i<=n;++i) nv[i]=mul(mod-mod/i,nv[mod%i]);
ans=nv[n];
for(i=1;i<ky;++i) ans=mul(dc(1,ans),nv[n-i-1]);
if(ky!=0) ans=dc(1,ans);
printf("%d\n",ans);
}