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

版权声明:侵删,转载请附带链接或评论 https://blog.csdn.net/corsica6/article/details/81515028

传送门:luoguT39018


题解

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

这题一看是个 d p ,设 f [ i ] [ j ] 表示在第 i 轮后,已更换 j 次的蒙对的最大概率。
转移方程似乎是这样的: f [ i ] [ j ] = m a x ( f [ i 1 ] [ j ] , ( 1 f [ i 1 ] [ j 1 ] ) / ( n i 1 ) )
先不说取模的问题,光这个方程都是错的(摔)。
考虑这一轮不换,自然是从 f [ i 1 ] [ j ] 转移,但换的情况按上式转移明显是错的,设 g [ i ] [ j ] 表示在 i 轮后更换 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 ] = m i n ( g [ i 1 ] [ j ] , ( 1 f [ i 1 ] [ j 1 ] ) / ( n i + 1 ) )
这样 O ( n 2 ) d p 50 p t s
但是模意义下表达的分式无法判断大小,所以还得另存两个 d o u b l e 的数组存原值判大小,这边模意义下的就按照这两个原值数组的操作进行处理。

下面说说奇妙的正解。

先考虑 k = 1 的情况,假设进行了某次操作后还剩下 i 个选项,蒙对的概率为 p ,那么若这次操作更换了选项,更换后的概率即为 1 p i 1
既然 k = 1 ,先假设在第 x 次更换选项,则需要保证 p 尽可能小,而 p 的下界即为 g [ x ] [ 0 ] ,这个值是唯一确定的,即 1 n
那么当 x = n 2 时, 1 p i 1 中的 i 达到了最小值 2 ,所以证明了 k = 1 时,把更改操作放在最后一轮可以得到最大的蒙对概率。

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

那么综上,对于任意满足条件的 n , k   ( k > 0 ) 均成立一下做法得到最大的蒙对概率:
k 1 轮更换选项,最后一轮更换选项。


代码

50 p t s 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;
}

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);
} 

猜你喜欢

转载自blog.csdn.net/corsica6/article/details/81515028
今日推荐