传送门:luoguT39018
题解
k=0 k = 0 时直接输出 n n 的逆元。
这题一看是个,设
f[i][j]
f
[
i
]
[
j
]
表示在第
i
i
轮后,已更换次的蒙对的最大概率。
转移方程似乎是这样的:
f[i][j]=max(f[i−1][j],(1−f[i−1][j−1])/(n−i−1))
f
[
i
]
[
j
]
=
m
a
x
(
f
[
i
−
1
]
[
j
]
,
(
1
−
f
[
i
−
1
]
[
j
−
1
]
)
/
(
n
−
i
−
1
)
)
。
先不说取模的问题,光这个方程都是错的(摔)。
考虑这一轮不换,自然是从
f[i−1][j]
f
[
i
−
1
]
[
j
]
转移,但换的情况按上式转移明显是错的,设
g[i][j]
g
[
i
]
[
j
]
表示在
i
i
轮后更换次的蒙对的最小概率,从
(1−g[i−1][j−1])/(n−i−1)
(
1
−
g
[
i
−
1
]
[
j
−
1
]
)
/
(
n
−
i
−
1
)
转移过来才是最优的。
于是转移方程变成了两个:
f[i][j]=max(f[i−1][j],(1−g[i−1][j−1])/(n−i−1))
f
[
i
]
[
j
]
=
m
a
x
(
f
[
i
−
1
]
[
j
]
,
(
1
−
g
[
i
−
1
]
[
j
−
1
]
)
/
(
n
−
i
−
1
)
)
g[i][j]=min(g[i−1][j],(1−f[i−1][j−1])/(n−i+1))
g
[
i
]
[
j
]
=
m
i
n
(
g
[
i
−
1
]
[
j
]
,
(
1
−
f
[
i
−
1
]
[
j
−
1
]
)
/
(
n
−
i
+
1
)
)
这样
O(n2)dp
O
(
n
2
)
d
p
有
50pts
50
p
t
s
。
但是模意义下表达的分式无法判断大小,所以还得另存两个
double
d
o
u
b
l
e
的数组存原值判大小,这边模意义下的就按照这两个原值数组的操作进行处理。
下面说说奇妙的正解。
先考虑
k=1
k
=
1
的情况,假设进行了某次操作后还剩下
i
i
个选项,蒙对的概率为,那么若这次操作更换了选项,更换后的概率即为
1−pi−1
1
−
p
i
−
1
。
既然
k=1
k
=
1
,先假设在第
x
x
次更换选项,则需要保证尽可能小,而
p
p
的下界即为,这个值是唯一确定的,即
1n
1
n
。
那么当
x=n−2
x
=
n
−
2
时,
1−pi−1
1
−
p
i
−
1
中的
i
i
达到了最小值,所以证明了
k=1
k
=
1
时,把更改操作放在最后一轮可以得到最大的蒙对概率。
当
k>1
k
>
1
时
观察之前列的
dp
d
p
式子:
g[i][j]=min(g[i−1][j],(1−f[i−1][j−1])/(n−i+1))
g
[
i
]
[
j
]
=
m
i
n
(
g
[
i
−
1
]
[
j
]
,
(
1
−
f
[
i
−
1
]
[
j
−
1
]
)
/
(
n
−
i
+
1
)
)
可以发现当进行到第
x
x
次操作时,必然得到的是
g[i][j](1≤i≤x)
g
[
i
]
[
j
]
(
1
≤
i
≤
x
)
的前缀最小值。
那么最后一次更换操作后得到
1−pi−1
1
−
p
i
−
1
,
p
p
在时所达到下界
g[n−2][k]
g
[
n
−
2
]
[
k
]
必然是全局最小的,所以
1−pi−1
1
−
p
i
−
1
在最后一次操作达到了最大,进而说明最后一次更换选项必然要放在最后一轮。
那么考虑剩下的
k−1
k
−
1
次操作,需要使操作后的
p
p
尽量小。
考虑连续两次的操作后使初始,变为
1−1−pi−1i−2=1i−1+p(i−1)(i−2)
1
−
1
−
p
i
−
1
i
−
2
=
1
i
−
1
+
p
(
i
−
1
)
(
i
−
2
)
。
同样这里考虑一次更改操作都没有前的
p
p
是唯一确定的,即,所以只需要让
i
i
尽量大。而我们可以把前次操作看做连续的两次操作的叠加,所以把这
k−1
k
−
1
次更改分别放在前
k−1
k
−
1
轮进行即可使最后得到的
p
p
最小。
那么综上,对于任意满足条件的均成立一下做法得到最大的蒙对概率:
前
k−1
k
−
1
轮更换选项,最后一轮更换选项。
代码
50pts 50 p t s 的 dp d p
#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;
}
100pts 100 p t s 的找规律
#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);
}