【洛谷】月赛:Tomoya loves Nagisa-DP&找规律

96 篇文章 0 订阅
10 篇文章 0 订阅

传送门:luoguT39018


题解

k=0 k = 0 时直接输出 n n 的逆元。

这题一看是个dp,设 f[i][j] f [ i ] [ j ] 表示在第 i i 轮后,已更换j次的蒙对的最大概率。
转移方程似乎是这样的: f[i][j]=max(f[i1][j],(1f[i1][j1])/(ni1)) f [ i ] [ j ] = m a x ( f [ i − 1 ] [ j ] , ( 1 − f [ i − 1 ] [ j − 1 ] ) / ( n − i − 1 ) )
先不说取模的问题,光这个方程都是错的(摔)。
考虑这一轮不换,自然是从 f[i1][j] f [ i − 1 ] [ j ] 转移,但换的情况按上式转移明显是错的,设 g[i][j] g [ i ] [ j ] 表示在 i i 轮后更换j次的蒙对的最小概率,从 (1g[i1][j1])/(ni1) ( 1 − g [ i − 1 ] [ j − 1 ] ) / ( n − i − 1 ) 转移过来才是最优的。
于是转移方程变成了两个:
f[i][j]=max(f[i1][j],(1g[i1][j1])/(ni1)) f [ i ] [ j ] = m a x ( f [ i − 1 ] [ j ] , ( 1 − g [ i − 1 ] [ j − 1 ] ) / ( n − i − 1 ) )
g[i][j]=min(g[i1][j],(1f[i1][j1])/(ni+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 个选项,蒙对的概率为p,那么若这次操作更换了选项,更换后的概率即为 1pi1 1 − p i − 1
既然 k=1 k = 1 ,先假设在第 x x 次更换选项,则需要保证p尽可能小,而 p p 的下界即为g[x][0],这个值是唯一确定的,即 1n 1 n
那么当 x=n2 x = n − 2 时, 1pi1 1 − p i − 1 中的 i i 达到了最小值2,所以证明了 k=1 k = 1 时,把更改操作放在最后一轮可以得到最大的蒙对概率。

k>1 k > 1
观察之前列的 dp d p 式子:
g[i][j]=min(g[i1][j],(1f[i1][j1])/(ni+1)) g [ i ] [ j ] = m i n ( g [ i − 1 ] [ j ] , ( 1 − f [ i − 1 ] [ j − 1 ] ) / ( n − i + 1 ) )
可以发现当进行到第 x x 次操作时,g[x][j](1jk)必然得到的是 g[i][j](1ix) g [ i ] [ j ] ( 1 ≤ i ≤ x ) 的前缀最小值。
那么最后一次更换操作后得到 1pi1 1 − p i − 1 p p x=n2时所达到下界 g[n2][k] g [ n − 2 ] [ k ] 必然是全局最小的,所以 1pi1 1 − p i − 1 在最后一次操作达到了最大,进而说明最后一次更换选项必然要放在最后一轮。
那么考虑剩下的 k1 k − 1 次操作,需要使操作后的 p p 尽量小。
考虑连续两次的操作后使初始p,变为 11pi1i2=1i1+p(i1)(i2) 1 − 1 − p i − 1 i − 2 = 1 i − 1 + p ( i − 1 ) ( i − 2 )
同样这里考虑一次更改操作都没有前的 p p 是唯一确定的,即1n,所以只需要让 i i 尽量大。而我们可以把前k1次操作看做连续的两次操作的叠加,所以把这 k1 k − 1 次更改分别放在前 k1 k − 1 轮进行即可使最后得到的 p p 最小。

那么综上,对于任意满足条件的n,k (k>0)均成立一下做法得到最大的蒙对概率:
k1 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);
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值