题意:一个细胞自动机(名字真高级)包含n个格子,每个格子的值为0~m-1。给定距离d,每次操作后每个格子的值将变为到它的距离不超过d的所有格子在操作之前的和模m。。求操作k次后每个格子的值。
思路:在白书上看了题目,马上敲了一份矩阵快速幂代码,发现在matrix结构体里面开500*500的数组的话,程序根本运行不起来,因为局部变量占用了过多的内存。然后看了下白书的思路,居然有一个结论,用于变换的矩阵肯定是形如
1 1 0 0 1
1 1 1 0 0
0 1 1 1 0
0 0 1 1 1
1 1 0 0 1
的,像这样每行循环右移的矩阵称为循环矩阵。循环矩阵的积还是循环矩阵。。因此,只需要计算矩阵的第一行就可以了。
发几个需要注意的地方:
1.矩阵元素相乘会溢出int。
2.在取模前一定要确保那个数不是负数。
3.一定要仔细推演原矩阵i行j列的元素等于第一行的哪一个。
#include <iostream>
#include <stdio.h>
#include <cmath>
#include <algorithm>
#include <iomanip>
#include <cstdlib>
#include <string>
#include <string.h>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <assert.h>
#include <set>
#include <ctype.h>
#define ll long long
#define max3(a,b,c) max(a,max(b,c))
using namespace std;
int n,m,d,k;
struct mat{
ll v[1][512];
mat(){
memset(v,0,sizeof(v));
}
};
mat mat_mul(mat a,mat b,int siz){
mat re;
for(int i=0;i<1;i++){
for(int j=0;j<siz;j++){
for(int k=0;k<siz;k++){
re.v[i][j]+=a.v[i][k]*b.v[i][(siz+j-k)%siz];
re.v[i][j]%=m;
}
}
}
return re;
}
mat mat_pow(mat m,int n,int siz){
mat re;
mat tmp=m;
re.v[0][0]=1;
while(n){
if(n&1){
re=mat_mul(re,tmp,siz);
}
tmp=mat_mul(tmp,tmp,siz);
n>>=1;
}
return re;
}
int main(){
while(cin>>n>>m>>d>>k){
ll vec[512];
for(int i=0;i<n;i++){
cin>>vec[i];
}
mat a;
for(int j=0;j<=d;j++){
a.v[0][(j)%n]=1;
a.v[0][(n-j)%n]=1;
}
mat ak=mat_pow(a,k,n);
for(int i=0;i<n;i++){
if(i)cout<<" ";
ll ans=0;
for(int j=0;j<n;j++){
ans+=ak.v[0][(j+n-i)%n]*vec[j];
ans%=m;
}
cout<<ans;
}
cout<<endl;
}
return 0;
}