[LOJ#2325]「清华集训 2017」小Y和恐怖的奴隶主
试题描述
"A fight? Count me in!" 要打架了,算我一个。
"Everyone, get in here!" 所有人,都过来!
小Y是一个喜欢玩游戏的OIer。一天,她正在玩一款游戏,要打一个Boss。
虽然这个Boss有 \(10^{100}\) 点生命值,但它只带了一个随从——一个只有 \(m\) 点生命值的“恐怖的奴隶主”。
这个“恐怖的奴隶主”有一个特殊的技能:每当它被扣减生命值但没有死亡(死亡即生命值 \(\leq 0\)),且Boss的随从数量小于上限 \(k\),便会召唤一个新的具有 \(m\) 点生命值的“恐怖的奴隶主”。
现在小Y可以进行 \(n\) 次攻击,每次攻击时,会从Boss以及Boss的所有随从中的等概率随机选择一个,并扣减 \(1\) 点生命值,她想知道进行 \(n\) 次攻击后扣减Boss的生命值点数的期望。为了避免精度误差,你的答案需要对 \(998244353\) 取模。
输入
输入第一行包含三个正整数 \(T, m, k\),\(T\) 表示询问组数,\(m, k\) 的含义见题目描述。
接下来 \(T\) 行,每行包含一个正整数 \(n\),表示询问进行 \(n\) 次攻击后扣减Boss的生命值点数的期望。
输出
输出共 \(T\) 行,对于每个询问输出一行一个非负整数,表示该询问的答案对 \(998244353\) 取模的结果。
可以证明,所求期望一定是一个有理数,设其为 \(p / q\)(\(\mathrm{gcd}(p,q) = 1\)),那么你输出的数 \(x\) 要满足 \(p \equiv qx (mod\ 998244353)\)。
输入示例
3 2 6
1
2
3
输出示例
499122177
415935148
471393168
数据规模及约定
在所有测试点中,\(1 \leq T \leq 1000, 1 \leq n \leq {10}^{18}, 1 \leq m \leq 3, 1 \leq k \leq 8\)。
题解
搜一下发现状态数最多只有 \(164\) 个,那就矩阵快速幂求每种状态 \(n\) 次攻击后的概率,期望每步累加即可。
这真是道神尼玛辣鸡卡常题啊!
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <vector>
#include <map>
using namespace std;
#define rep(i, s, t) for(int i = (s); i <= (t); i++)
#define dwn(i, s, t) for(int i = (s); i >= (t); i--)
#define LL long long
LL read() {
LL x = 0, f = 1; char c = getchar();
while(!isdigit(c)){ if(c == ‘-‘) f = -1; c = getchar(); }
while(isdigit(c)){ x = x * 10 + c - ‘0‘; c = getchar(); }
return x * f;
}
#define maxn 15
#define maxs 167
#define maxq 1010
#define MOD 998244353
#define maxlog 60
int cnts, m, K;
vector <int> sta[maxs];
map <vector <int>, int> id;
void dfs(vector <int> A) {
if(id.count(A)) return ;
sta[id[A] = ++cnts] = A;
int p = K;
rep(i, 0, K - 1) if(!A[i]){ p = i; break; }
rep(i, 0, p - 1) {
vector <int> B = A;
B[i]--;
if(B[i] && p <= K - 1) B[p] = m;
sort(B.begin(), B.end());
rep(j, 0, K - 1 >> 1) swap(B[j], B[K-1-j]);
dfs(B);
}
return ;
}
int inv[maxn];
LL buf[maxs];
struct Matrix {
int n, m, A[maxs][maxs];
Matrix() {}
Matrix(int _, int __): n(_), m(__) {}
Matrix operator * (const Matrix& t) const {
Matrix ans(n, t.m);
rep(i, 1, ans.n) {
memset(buf, 0, sizeof(buf));
const int *a = A[i];
rep(k, 1, m) {
const int *ta = t.A[k];
LL tmp = *(a + k);
rep(j, 1, ans.m) buf[j] += tmp * *(ta + j) % MOD;
}
rep(j, 1, ans.m) ans.A[i][j] = buf[j] % MOD;
}
return ans;
}
Matrix operator = (const Matrix& t) {
n = t.n; m = t.m;
rep(i, 1, cnts + 1) memcpy(A[i], t.A[i], sizeof(t.A[i]));
return *this;
}
void print() {
printf("Matrix(%d, %d)\n", n, m);
rep(i, 1, n) rep(j, 1, m) printf("%d%c", A[i][j], j < m ? ‘ ‘ : ‘\n‘);
return ;
}
} tr, mpow[maxlog];
int Ans[maxs];
LL Que[maxq];
int main() {
int T = read(); m = read(); K = read();
vector <int> A(K);
rep(i, 0, K - 1) A[i] = 0; A[0] = m;
dfs(A);
// printf("cnts: %d\n", cnts);
/*rep(i, 1, cnts) {
printf("sta[%d]: ", i);
rep(j, 0, K - 1) printf("%d%c", sta[i][j], j < K - 1 ? ‘ ‘ : ‘\n‘);
} // */
inv[1] = 1;
rep(i, 2, K + 1) inv[i] = (LL)(MOD - MOD / i) * inv[MOD%i] % MOD;
tr = Matrix(cnts + 1, cnts + 1);
rep(s, 1, cnts) {
A = sta[s];
int p = K;
rep(i, 0, K - 1) if(!A[i]){ p = i; break; }
rep(i, 0, p - 1) {
vector <int> B = A;
B[i]--;
if(B[i] && p <= K - 1) B[p] = m;
sort(B.begin(), B.end());
rep(j, 0, K - 1 >> 1) swap(B[j], B[K-1-j]);
(tr.A[s][id[B]] += inv[p+1]) %= MOD;
}
tr.A[s][s] += inv[p+1];
if(tr.A[s][s] >= MOD) tr.A[s][s] -= MOD;
tr.A[s][cnts+1] += inv[p+1];
if(tr.A[s][cnts+1] >= MOD) tr.A[s][cnts+1] -= MOD;
}
tr.A[cnts+1][cnts+1] = 1;
LL mx = 0;
rep(q, 1, T) Que[q] = read(), mx = max(mx, Que[q]);
mpow[0] = tr;
mx >>= 1;
for(int i = 1; mx; mx >>= 1, i++) mpow[i] = mpow[i-1] * mpow[i-1];
rep(kase, 1, T) {
LL n = Que[kase];
memset(Ans, 0, sizeof(Ans)); Ans[1] = 1;
int t = 0;
while(n) {
if(n & 1) {
memset(buf, 0, sizeof(buf));
rep(k, 1, cnts + 1) {
const int *a = mpow[t].A[k];
LL tmp = Ans[k];
rep(j, 1, cnts + 1) buf[j] += tmp * *(a + j) % MOD;
}
rep(i, 1, cnts + 1) Ans[i] = buf[i] % MOD;
}
t++; n >>= 1;
}
printf("%d\n", Ans[cnts+1]);
}
return 0;
}