CodeForces 1864H Asterism Stream

发布时间 2023-10-04 16:59:59作者: zltzlt

洛谷传送门

CF 传送门

好题。

考虑计算 \(x\) 落在 \([1, n - 1]\) 的概率。设 \(f_i\)\(x\) 经过 \(i\) 的概率,答案即为 \(\sum\limits_{i = 1}^{n - 1} f_i\)

\(f\) 有一个朴素的递推:

\[f_i = \begin{cases} \frac{1}{2} (f_{i - 1} + f_{\frac{i}{2}}) & 2 \mid i \\ \frac{1}{2} f_{i - 1} & 2 \nmid i \end{cases} \]

初值为 \(f_1 = 1\)

发现每个状态的前驱数量很少,可以往矩阵优化上考虑。

但是当 \(i\) 为偶数的时候,我们需要知道 \(f_{\frac{i}{2}}\) 的值。当 \(\frac{i}{2}\) 为奇数时,\(f_i = \frac{1}{2} (f_{i - 1} + \frac{1}{2} f_{\frac{i}{2} - 1}) = \frac{1}{2} f_{i - 1} + \frac{1}{4} f_{\frac{i}{2} - 1}\),否则继续展开:

\[\begin{aligned} f_i & = \frac{1}{2} f_{i - 1} + \frac{1}{2} f_{\frac{i}{2}} \\ & = \frac{1}{2} f_{i - 1} + \frac{1}{4} (f_{\frac{i}{2} - 1} + f_{\frac{i}{4}})\end{aligned} \]

到这里还要继续讨论 \(\frac{i}{4}\) 的奇偶性,如果是偶数还要继续展开。于是可以发现,若设 \(\text{low}(i) = k\)\(\text{low}(i)\)\(i\) 的最低位从低到高从 \(0\) 开始的编号),那么:

\[f_i = \sum\limits_{j = 0}^k \frac{1}{2^{j + 1}} f_{\frac{i}{2^j} - 1} = \sum\limits_{j = 0}^k \frac{1}{2^{j + 1}} f_{\left\lfloor\frac{i - 1}{2^j}\right\rfloor} \]

为了防止对边界的讨论,我们规定 \(f_0 = 2\),因为 \(f_1 = \frac{1}{2} f_0 = 1\)

于是,我们在答案向量中维护 \(\forall j \in [0, \log i], f_{\left\lfloor\frac{i}{2^j}\right\rfloor}\)。容易发现对于 \(\text{low}(i) = k\) 相同的 \(i\),转移是相同的,都形如 \(\forall j \in [0, k], f_{\frac{i}{2^j}}\) 要从 \(\forall l \in [j, k], f_{\left\lfloor\frac{i - 1}{2^l}\right\rfloor}\) 转移过来。

于是我们设 \(\text{low}(i) = k\) 的所有 \(i\) 的转移矩阵为 \(F_k\)\(i\) 的答案向量为 \(A_i\),那么 \(A_i = A_{i - 1} F_{\text{low}(i)}\)\(\forall j \in [0, \log n], A_{0, j} = 2\)。还要在转移的过程维护 \(\sum f\),在 \(A_{i, 60}\) 处维护即可。

考虑像矩阵快速幂一样预处理。设 \(G_i = \prod\limits_{j = 1}^{2^i} F_{\text{low}(j)}, H_i = \prod\limits_{j = 1}^{2^{i + 1} - 1} F_{\text{low}(j)}\)。初始有 \(G_0 = H_0 = F_0\)。然后可以递推,\(G_i = H_{i - 1} F_i, H_i = G_i H_{i - 1}\)

然后回答 \(n\) 时,维护一个答案向量 \(A\),然后按 \(2\) 的次方分段计算即可。

时间复杂度 \(O((T + \log n) \log^3 n)\)

code
// Problem: H. Asterism Stream
// Contest: Codeforces - Harbour.Space Scholarship Contest 2023-2024 (Div. 1 + Div. 2)
// URL: https://codeforces.com/problemset/problem/1864/H
// Memory Limit: 256 MB
// Time Limit: 3000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<ll, ll> pii;

const int maxn = 63;
const ll mod = 998244353;
const ll inv2 = (mod + 1) / 2;

struct mat {
	ll a[maxn][maxn];
	mat() {
		mems(a, 0);
	}
} f[maxn], g[maxn], h[maxn];

inline mat operator * (const mat &a, const mat &b) {
	mat res;
	for (int i = 0; i < maxn; ++i) {
		for (int j = 0; j < maxn; ++j) {
			for (int k = 0; k < maxn; ++k) {
				res.a[i][j] = (res.a[i][j] + a.a[i][k] * b.a[k][j]) % mod;
			}
		}
	}
	return res;
}

struct vec {
	ll a[maxn];
	vec() {
		mems(a, 0);
	}
};

inline vec operator * (const vec &a, const mat &b) {
	vec res;
	for (int i = 0; i < maxn; ++i) {
		for (int j = 0; j < maxn; ++j) {
			res.a[i] = (res.a[i] + a.a[j] * b.a[j][i]) % mod;
		}
	}
	return res;
}

inline void init() {
	for (int i = 0; i < 60; ++i) {
		for (int j = 0; j <= 60; ++j) {
			f[i].a[j][j] = 1;
		}
		f[i].a[0][60] = 1;
		for (int j = 0; j <= i; ++j) {
			ll coef = 1;
			for (int k = j; k <= i; ++k) {
				coef = coef * inv2 % mod;
				f[i].a[k][j] = coef;
			}
		}
	}
	g[0] = h[0] = f[0];
	for (int i = 1; i < 60; ++i) {
		g[i] = h[i - 1] * f[i];
		h[i] = g[i] * h[i - 1];
	}
}

void solve() {
	ll n;
	scanf("%lld", &n);
	vec ans;
	for (int i = 0; i < 60; ++i) {
		ans.a[i] = 2;
	}
	for (int i = 59; ~i; --i) {
		if (n & (1LL << i)) {
			ans = ans * g[i];
		}
	}
	printf("%lld\n", (ans.a[60] + mod - 2) % mod);
}

int main() {
	init();
	int T = 1;
	scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}