KYOCERA Programming Contest 2022(AtCoder Beginner Contest 271)G.Access Counter(概率+矩阵快速幂优化dp)

题目

Takahashi布置了一个网页,

给定一个长为24的仅由A和T构成的字符串,表示每天整点能登进这个网页的概率

第i个字母如果是T,表示第i个整点的时候Takahashi有X(1<=X<=99)%的概率能登进去一次,

如果是A,表示第i个整点的时候Aoki有Y(1<=Y<=99)%的概率能登进去一次,

问如果在第一天的0点前把网页挂上去,第n(1<=n<=1e18)次登陆是由Aoki登进去的概率,

答案p/q,对998244353取模

思路来源

官方题解

题解

感觉主要是第一步概率化简就没想到,但是也是很基础的式子

首先要求第一次登陆是发生在第i小时的概率,

即等于第一天在第i小时登陆+第二天在第i小时登陆+...+第无穷天在第i小时成功登陆

而第二天在第i小时登陆的概率=第一天在第i小时登陆的概率*这一整天都没登进去的概率

是一个等比数列求和,考虑计q=这一整天都没登进去的概率

p1=第一天在第i小时登陆的概率

则ans[i]=p1(1+q+q^2+...)=p1*(1-q^无穷)/(1-q),

又24小时内的事件x、y的概率都大于0,小于1,所以q也在0和1之间,有ans[i]=p1/(1-q)

然后注意到计算p1的时候,实际是默认上一次登录是发生在23点的(0点前)

但有了一次登录之后,对于下一次登录来说,上一次登录就可能发生在[0,23]点的任意一个时刻了

所以,需要预处理dp[i][j]表示上一次登录是在第i时刻时,本次登录是在第j时刻的概率

这个时候,p1这个概率,也就是[i+1,j-1]时刻都登陆失败,而第j时刻登陆成功,

需要考虑跨天的情形

要求第n(n<=1e18)次登录的时候,矩阵快速幂优化一下就可以了,这里写成了类似快速幂的样子

第n次还需要是Aoki登录,所以只统计第n次时,字母是A的答案

代码

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=65,H=25,mod=998244353;
ll n;
int x,y,q,inv,sum,p[H];
int dp[N][H][H],ans[H],nans[H];
char s[H];
int modpow(int x,int n,int mod){
    int res=1;
    for(;n;n>>=1,x=1ll*x*x%mod){
        if(n&1)res=1ll*res*x%mod;
    }
    return res;
}
int cal(){
    ans[23]=1;
    for(int i=0;n;n>>=1,i++){
        if(n&1){
            memset(nans,0,sizeof nans);
            for(int j=0;j<24;++j){
                for(int k=0;k<24;++k){
                    nans[k]=(nans[k]+1ll*ans[j]*dp[i][j][k]%mod)%mod;
                }
            }
            memcpy(ans,nans,sizeof ans);
        }
        for(int j=0;j<24;++j){
            for(int k=0;k<24;++k){
                for(int l=0;l<24;++l){
                    dp[i+1][j][l]=(dp[i+1][j][l]+1ll*dp[i][j][k]*dp[i][k][l]%mod)%mod;
                }
            }
        }
    }
    int res=0;
    for(int i=0;i<24;++i){
        if(s[i]=='A'){
            res=(res+ans[i])%mod;
        }
    }
    return res;
}
int main(){
    cin>>n>>x>>y;
    cin>>s;
    q=1;
    inv=modpow(100,mod-2,mod);
    for(int j=0;j<24;++j){ 
        int v=(s[j]=='A')?y:x;
        p[j]=1ll*v*inv%mod;
        q=1ll*q*(1-p[j]+mod)%mod;
    }
    sum=modpow(1-q+mod,mod-2,mod);//q<1 1*(1-q^n)/(1-q)
    for(int las=0;las<24;++las){
        int pre=1;
        for(int add=0;add<24;++add){
            int now=(las+add+1)%24;
            int can=1ll*pre*p[now]%mod;
            dp[0][las][now]=1ll*sum*can%mod;
            pre=1ll*pre*(1-p[now]+mod)%mod;
        }
    }    
    cout<<cal()<<endl;
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值