题目链接
题目大意
给定一个n格的环,现在有个距离d,每次变化把环和他周围距离d以内的格子相加,结果mod m,问经过k次变换之后,环上的各个数字
题目思路
前置知识
循环矩阵它的行向量的每个元素都是前一个行向量各元素依次右移一个位置得到的结果。图片如下
性质
循环矩阵遵循代数运算法则。对于两个循环矩阵 A 与 B 来说,A + B 也是循环矩阵。AB 也是循环矩阵,并且 AB=BA。
正文
很显然,每一个状态都是和上一个状态有关。而且看数据范围显然是矩阵快速幂。
矩阵很好想,每个位置对应周围几个位置为1,其余位置为0,但是这个矩阵有500,有点大,直接n^3 去求矩阵不太合适,然后观察发现这个矩阵是个循环矩阵,循环矩阵相乘的话,只需要保存一行即可,然后用n^2的时间就足够计算了
代码
//这个矩阵乘法的写法可以学习
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 505;
int n, m, d, k;
ll ans[maxn], matrix[maxn];
ll c[maxn+5];
void mul(ll a[], ll b[]) {
memset(c, 0, sizeof(c));
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
c[i] += a[j] * b[(i-j+n) % n];
for (int i = 0; i < n; i++)
b[i] = c[i] % m;
}
int main() {
while (scanf("%d%d%d%d", &n, &m, &d, &k) != EOF) {
memset(ans, 0, sizeof(ans));
memset(matrix, 0, sizeof(matrix));
for (int i = 0; i < n; i++)
cin>>ans[i];
for (int i = 0; i <= n-1; i++)
matrix[i] = (i<=d||n-i<=d);
while (k) {
if (k & 1){
mul(matrix, ans);
}
mul(matrix, matrix);
k >>= 1;
}
for (int i = 0; i <= n - 1; i++)
printf("%lld%c", ans[i],i==n-1?'\n':' ');
}
return 0;
}