题解 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值