P5550 Chino的数列 题解
提供一个只需要推一个转移矩阵的做法。
思路
$n$ 很小,$k$ 很大,考虑矩阵加速递推。为了方便叙述,下文默认 $n=4,s=1,m=2$。
设 $\{a_n\}$ 经过一组操作后变为 $\{b_n\}$。定义转移矩阵 $P$,使得 $\begin{bmatrix}a_1\cdots a_n\end{bmatrix}\times P=\begin{bmatrix}b_1\cdots b_n\end{bmatrix}$。
如果只有平移操作,转移矩阵很显然:
$$\begin{bmatrix}0&0&0&1\\1&0&0&0\\0&1&0&0\\0&0&1&0\end{bmatrix}$$
如果先平移再交换,交换第 $s$ 列与第 $m$ 列即可:
$$\begin{bmatrix}0&0&0&1\\0&1&0&0\\1&0&0&0\\0&0&1&0\end{bmatrix}$$
考虑把先交换再平移转换为先平移再交换。
记 $s',m'$ 为 $s,m$ 向前平移后的位置,则 $s'=(s+n-2)\bmod n+1,m'=(m+n-2)\bmod n+1$。
容易发现,交换第 $s'$ 列与第 $m'$ 列即可。
$n=4,s=1,m=2$ 时,$s'=4,m'=1$,转移矩阵为:
$$\begin{bmatrix}1&0&0&0\\0&0&0&1\\0&1&0&0\\0&0&1&0\end{bmatrix}$$
代码
#include <cstdio>
#include <cstring>
#define F(x) for(int x = 1;x <= n;++x)
int n, s, m;long long k;
struct S
{
long long a[100][100];S() {memset(a, 0, sizeof a);}S operator*(S b)
{S c;F(k) F(i) F(j) c.a[i][j] += a[i][k] * b.a[k][j];return c;}
}p, q;
int main()
{
scanf("%d%d%d%lld", &n, &s, &m, &k);
s = (s + n - 2) % n + 1;m = (m + n - 2) % n + 1;
for(int i = 1;i <= n;++i)
{
scanf("%lld", &q.a[1][i]);if(i == s) p.a[m % n + 1][i] = 1;
else if(i == m) p.a[s % n + 1][i] = 1;else p.a[i % n + 1][i] = 1;
}
for(;k;k >>= 1, p = p * p) if(k & 1) q = q * p;
for(int i = 1;i <= n;++i) printf("%lld ", q.a[1][i]);return 0;
}

浙公网安备 33010602011771号