题解 AT_abc270_h【[ABC270Ex] add 1】
WXQ 给我看的题,想了下就会了,然后调了一年(拷的矩阵板子没 memset)。
最近学习了根据鞅与鞅的停时定理构造势能函数来解决这类操作结束时间的期望的问题。但这道题并不能用这个做法,主要的原因在于终止状态的统计学性质不够优秀,结束状态有无穷多个。
我们考虑观察下性质。首先,如果一个操作序列 \(B_1, \cdots, B_n\) 满足条件当且仅当 \(\forall i, n - i \ge A_{B_i}\)。我们发现对于当前的一段操作,最短的结束时间为 \(\max_{i = 1}^{n}\limits A_{B_i} - (n - i)\)。于是,一次对 \(A_i\) 的操作就相当于对当前的所有数减一,并且在序列中新加入一个数 \(A_i\),结束状态为所有数小于等于 \(0\)。
设 \(f_i\) 表示当前序列中的最大值为 \(i\) 的期望结束时间,暴力枚举下一次操作是对哪个数操作:
\[f_i=\sum_{i = 1}^{n} f_{\max(i - 1, A_i)}
\]
考虑对 \(a_i \le i - 1\) 和 \(a_i\ge i\) 分开来,设 \(s_i\) 表示 \(A_i\) 中小于等于 \(i-1\) 的个数:
\[f_i=s_i \times f_{i - 1} + \sum_{i = s_i + 1}^{n} f_{A_i}
\]
考虑主元法可以求出目标 \(f_{\max_{i=1}^{n}\limits A_i}\),但这道题 \(A_i\) 的范围很大。我们发现对于 \([A_i, A_{i+1})\) 的状态,转移是一样的,于是考虑矩阵乘法优化这个过程,复杂度 \(O(n \log A)\)。
#include <bits/stdc++.h>
#define int long long
#define SZ(x) (int) x.size() - 1
#define all(x) x.begin(), x.end()
#define ms(x, y) memset(x, y, sizeof x)
#define F(i, x, y) for (int i = (x); i <= (y); i++)
#define DF(i, x, y) for (int i = (x); i >= (y); i--)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
x = 0; int f = 1; char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
const int N = 2e5 + 10, MOD = 998244353;
int n, a[N];
pair <int, int> f[N], sum;
struct WXQ_matrix{
int n, m;
int a[5][5];
WXQ_matrix () {
ms(a, 0);
}
WXQ_matrix operator * (const WXQ_matrix &x) const{
assert(m == x.n);
WXQ_matrix z;
z.n = n; z.m = x.m;
F(k, 1, m)
F(i, 1, n)
F(j, 1, x.m)
z.a[i][j] = (z.a[i][j] + (ll) a[i][k] * x.a[k][j]) % MOD;
return z;
}
};
inline int Quickpow(int x, int y) {
int ans = 1;
for (; y; x = (ll) x * x % MOD,y >>= 1)
if (y & 1) ans = (ll) ans * x % MOD;
return ans;
}
signed main() {
read(n);
F(i, 1, n) read(a[i]);
sum.first = f[n].first = 1;
DF(i, n - 1, 1) {
int k = Quickpow(i, MOD - 2);
WXQ_matrix t, ans;
t.n = 2; t.m = 2;
t.a[1][1] = (ll) n * k % MOD; t.a[1][2] = 0;
t.a[2][1] = MOD - k; t.a[2][2] = 1;
ans.n = 2; ans.m = 2;
ans.a[1][1] = 1; ans.a[1][2] = 0;
ans.a[2][1] = 0; ans.a[2][2] = 1;
int kk = a[i + 1] - a[i];
for (; kk; kk >>= 1, t = t * t)
if (kk & 1) ans = ans * t;
f[i].first = ((ll) f[i + 1].first * ans.a[1][1] + (ll) sum.first * ans.a[2][1]) % MOD;
f[i].second = ((ll) f[i + 1].second * ans.a[1][1] + (ll) (sum.second + n) * ans.a[2][1]) % MOD;
sum.first = (sum.first + f[i].first) % MOD;
sum.second = (sum.second + f[i].second) % MOD;
}
cout << ((ll) -f[1].second * Quickpow(f[1].first, MOD - 2) % MOD + MOD) % MOD;
return 0;
}