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;
}
posted @ 2022-08-24 19:24  Jijidawang  阅读(15)  评论(0)    收藏  举报  来源