箱子

题目描述

小猪佩奇和其他n-1个小伙伴们正在玩一个老套的游戏。
一个房间中,有n个随机打乱过的箱子放成一排,每个箱子里有一张纸条,写着一个人的名字。每个人要按一定顺序走进房间,打开最多k个箱子,如果其中没有自己的名字游戏就失败了。每个人走出房间的时候需要关上箱子。(游戏中箱子的顺序不会再被调换)游戏前他们可以商量出一个策略,但是游戏开始之后他们不能互相交流。
小猪佩奇想知道最优策略下游戏成功,即每个人都找到写有自己名字的箱子的概率。

注:一个人每次打开箱子并非同时开启,而是依次打开。

做法

我们来考虑策略。
第i个人策略是这样的,打开i,如果a[i]!=i,打开a[i],如果a[a[i]]!=i,打开a[a[i]]……依次类推。
那么显然成功的概率就是排列形成的每个环大小都不超过k。
我们来证明这是最优策略。
考虑一个简单问题,每个人进去开了箱子就不闭上了。显然新问题获胜概率大于等于原问题。
在这个简单问题里,每个箱子都要开一次。每个人进去只会开没开的箱子,这些箱子不管哪个都是一样的,因此相当于随机开箱。
对于一种开箱方法,即一个人如果开出了数字s1~st(t<=k),我们可以构造p[s1]=s2,p[s2]=s3……p[st]=s1。
那么每种开箱方法对应了一个排列,这个排列形成的每个环大小都不超过k。
因此新问题的获胜概率就是排列形成的每个环大小都不超过k。这样我们也就证明了原问题的这个策略最优。
然后DP就很简单了。

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn=200000+10,mo=998244353;
int ni[maxn],p[maxn];
int i,j,k,l,t,n,m,ca,ans;
int qsm(int x,int y){
    if (!y) return 1;
    int t=qsm(x,y/2);
    t=(ll)t*t%mo;
    if (y%2) t=(ll)t*x%mo;
    return t;
}
int main(){
    freopen("box.in","r",stdin);freopen("box.out","w",stdout);
    fo(i,1,200000) ni[i]=qsm(i,mo-2);
    scanf("%d",&ca);
    while (ca--){
        scanf("%d%d",&n,&k);
        p[0]=1;
        fo(i,1,n){
            p[i]=p[i-1];
            if (i-k-1>=0) (p[i]-=p[i-k-1])%=mo;
            p[i]=(ll)p[i]*ni[i]%mo;
            (p[i]+=p[i-1])%=mo;
        }
        ans=(p[n]-p[n-1])%mo;
        (ans+=mo)%=mo;
        printf("%d\n",ans);
    }
}
阅读更多
版权声明:本文是蒟蒻写出来的,神犇转载也要说一声哦! https://blog.csdn.net/WerKeyTom_FTD/article/details/80407313
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭