CF1152F2 Neko Rules the Catniverse (Large Version) 题解

\(\text{CF1152F2 Neko Rules the Catniverse (Large Version) 题解}\)

这个题有点意思啊。

我们大胆猜想这个题的 dp 是从每个星球一个一个线性转移的。得到这个结论有两种方式:

法一:发现按照 Neko 飞行的轨迹直接 dp 是比较扯淡的,我们难以考虑无限制的走回头路的情况,无法记录前面的状态,而线性转移不需要考虑走回头路的情形。

法二:考虑 F1 和 F2 的唯一差别是 \(n\le 10^5\)\(n\le 10^9\),而回到原比赛,从 CF 的分数设置上来看 F2 只有低贱的 750 分,换句话说两个题之间没有什么不可逾越的鸿沟,大概率是一些小小优化,那傻子也能排除到只有一种情形:从 \(O(n)\) 的线性 dp 优化到 \(O(\log n)\) 的矩阵乘法优化 dp。

好了各位扯淡环节结束。要线性转移换句话说就是考虑当前这个位置插入在移动序列的哪个位置。那这个题其实就变得 naive 了:考虑到 \(i\) 位置前一个位置是哪个位置,显然只会有 \(O(m)\) 个位置,那么状压一下前 \(m\) 个位置是否走过即可,转移就考虑这个位置走还是不走,系数就是一个 \(\text{popc(S)}+1\) 的形式,\(+1\) 是要考虑 \(i\) 点做起点的情形,然后其实就做完了。

代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 225, mod = 1e9 + 7;
void add(int &x, int y) {
	x = x + y >= mod ? x + y - mod : x + y;
}
int n, k, m;
struct Mar {
	int a[N][N];
	Mar() {
		memset(a, 0, sizeof a);
	}
	Mar operator * (const Mar &x) const {
		Mar res;
		for (int i = 0; i < N; i++)
			for (int k = 0; k < N; k++)
				if (a[i][k])
					for (int j = 0; j < N; j++)
						add(res.a[i][j], a[i][k] * x.a[k][j] % mod);
		return res;
	}
} bas;
Mar qpow(Mar x, int y) {
	Mar ans;
	for (int i = 0; i < N; i++) ans.a[i][i] = 1;
	while (y) {
		if (y & 1) ans = ans * x;
		x = x * x;
		y >>= 1;
	}
	return ans;
}
int gwt(int x, int y) {
	return x * (1 << m) + y;
}

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> n >> k >> m;
	for (int j = 0; j <= k; j++)
		for (int s = 0; s < (1 << m); s++) {
			int t = __builtin_popcount(s) + 1, p = (1 << m) - 1;
			if (j < k) add(bas.a[gwt(j, s)][gwt(j + 1, ((s << 1) | 1) & p)], t);
			add(bas.a[gwt(j, s)][gwt(j, (s << 1) & p)], 1);
		}
	Mar fir;
	fir.a[0][gwt(0, 0)] = 1;
	fir = fir * qpow(bas, n);
	int ans = 0;
	for (int s = 0; s < (1 << m); s++) add(ans, fir.a[0][gwt(k, s)]);
	cout << ans << '\n';
	return 0;
}
posted @ 2025-10-26 21:35  长安19路  阅读(8)  评论(0)    收藏  举报