[ABC271G] Access Counter 题解
[ABC271G] Access Counter Solution
更好的阅读体验戳此进入
题面
每天存在 $ 24 $ 个时间点,两人要访问一个网站,给定一个 A
和 T
组成的长度为 $ 24 $ 的字符串,若为 T
则 Taka 有 $ X% $ 的概率成功访问,若为 A
则 Aoki 有 $ Y% $ 的概率成功访问,求该网站第 $ n $ 次被访问时是被 Aoki 访问的概率,对 $ 998244353 $ 取模。
Solution
首先看一眼这个题面与 $ n $ 的范围,就不难联想到矩阵快速幂,但是这题的难点我认为主要还是在状态的设计与转移上。
一个显而易见但较为重要的性质,即两次访问之间间隔的天数是易于统计的,重要的在于两次访问的时间点 $ i, j $ 之间的位置关系。
令对应时间点的访问成功概率为 $ p_i $,令一天中未发生任何访问的概率为 $ P = \prod_{i = 1}^{24}(1 - p_i) $,考虑预处理 $ dp(i, j) $ 表示上次在 $ i $ 访问本次在 $ j $ 访问的概率,如果两者之间的距离不超过一天,显然可以预处理,令 $ R $ 为从 $ i + 1 $ 到 $ j - 1 $ 的所有 $ 1 - p $ 之积,则转移显然,即:
显然和式为等比数列求和,且由题意可知公比 $ P \lt 0 $,则继续推式子可以得到:
显然 $ n \to +\infty $ 时 $ P^n \to 0 $,则转化为:
于是我们完成了 $ dp(i, j) $ 的预处理。
令 $ f(i, j) $ 表示第 $ i $ 次访问发生在 $ j $ 时刻的概率,有:
按照我们之前的想法挂到矩阵上:
显然 $ f(0, i) = [i = 24] $,初始时我们默认其最终选择的只能是 $ 24 $,等价于从 $ 0 $ 开始,这样不会对我们的结果造成影响,那么最终形式:
答案统计一下所有 $ i $ 处是 Aoki 的 $ f(n, i) $ 之和即可,可以矩阵快速幂优化,令 $ d = 24 $,则复杂度 $ O(d^3 \log n) $,可以通过。
Code
#define _USE_MATH_DEFINES
#include <bits/stdc++.h>
#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW void* Edge::operator new(size_t){static Edge* P = ed; return P++;}
#define ROPNEW_NODE void* Node::operator new(size_t){static Node* P = nd; return P++;}
using namespace std;
mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}
typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;
#define MOD (998244353ll)
template < typename T = int >
inline T read(void);
ll N;
ll Taka, Aoki;
ll p[30];
ll P(1);
ll ansv(0);
ll inv100;
ll dp[30][30];
bitset < 30 > belong;
ll qpow(ll a, ll b){
ll ret(1), mul(a);
while(b){
if(b & 1)ret = ret * mul % MOD;
b >>= 1;
mul = mul * mul % MOD;
}return ret;
}
class Matrix{
private:
int siz;
ll v[30][30];
public:
Matrix(int len = 24, int pat = 0){
siz = len;
for(int i = 0; i < siz; ++i)
for(int j = 0; j < siz; ++j)
v[i][j] = 0;
switch(pat){
case 1:{
for(int i = 0; i < siz; ++i)v[i][i] = 1;
break;
}
case 2:{
v[0][siz - 1] = 1;
break;
}
case 3:{
for(int i = 1; i <= siz; ++i)
for(int j = 1; j <= siz; ++j)
v[i - 1][j - 1] = dp[i][j];
break;
}
default: break;
}
}
friend Matrix operator * (const Matrix &a, const Matrix &b){
Matrix ret(24);
for(int i = 0; i < 24; ++i)
for(int j = 0; j < 24; ++j)
for(int k = 0; k < 24; ++k)
(ret.v[i][j] += a.v[i][k] * b.v[k][j] % MOD) %= MOD;
return ret;
}
void SetAns(void){
for(int i = 0; i < 24; ++i)
if(belong[i + 1])(ansv += v[0][i]) %= MOD;
}
void Print(void){
printf("##########\n");
for(int i = 0; i < siz; ++i)
for(int j = 0; j < siz; ++j)
printf("%lld%c", v[i][j], j == siz - 1 ? '\n' : ' ');
printf("##########\n");
}
}ans(24, 2);
Matrix qpow(Matrix a, ll b){
Matrix ret(24, 1), mul(a);
while(b){
if(b & 1)ret = ret * mul;
b >>= 1;
mul = mul * mul;
}return ret;
}
int main(){
inv100 = qpow(100, MOD - 2);
N = read < ll >(), Taka = read(), Aoki = read();
for(int i = 1; i <= 24; ++i){
char c = getchar(); while(!isalpha(c))c = getchar();
p[i] = c == 'T' ? Taka : Aoki, belong[i] = c == 'T' ? false : true;
(P *= (100 - p[i]) * inv100 % MOD) %= MOD;
}
for(int i = 1; i <= 24; ++i){
for(int j = 1; j <= 24; ++j){
ll R(1);
if(i < j)for(int k = i + 1; k <= j - 1; ++k)(R *= (100 - p[k]) * inv100 % MOD) %= MOD;
else{
for(int k = i + 1; k <= 24; ++k)(R *= (100 - p[k]) * inv100 % MOD) %= MOD;
for(int k = 1; k <= j - 1; ++k)(R *= (100 - p[k]) * inv100 % MOD) %= MOD;
}
dp[i][j] = R * (p[j] * inv100 % MOD) % MOD * qpow((1 - P + MOD) % MOD, MOD - 2) % MOD;
}
}
Matrix base(24, 3);
(ans * qpow(base, N)).SetAns();
printf("%lld\n", ansv);
fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
return 0;
}
template < typename T >
inline T read(void){
T ret(0);
int flag(1);
char c = getchar();
while(c != '-' && !isdigit(c))c = getchar();
if(c == '-')flag = -1, c = getchar();
while(isdigit(c)){
ret *= 10;
ret += int(c - '0');
c = getchar();
}
ret *= flag;
return ret;
}
UPD
update-2023_02_09 初稿