Description
一个细胞自动机包含n个格子,每个格子的取值为0~m-1。给定距离d,则每次操作后每个格子的值将变为到它距离不超过d的所有格子在操作之前的值之和除以m的余数,其中i和j的距离为min{|i-j|,n-|i-j|}。给定n,m,d,k和自动机各格子的初始值,你的任务是计算k次操作以后各格子的值。
如下图,n=5,m=3,d=1,一次操作将把图a变成图b。比如,与格子3距离不超过1的格子(即格子2,3,4)在操作之前的值分别是2,2,1,因此从 操作后格子3的值为(2+2+1)mod 3=2。
Input
第一行包含4个整数n,m,d,k。第二行包含n个0..m-1的整数,即各格子的初始值。
Output
一个细胞自动机包含n个格子,每个格子的取值为0~m-1。给定距离d,则每次操作后每个格子的值将变为到它距离不超过d的所有格子在操作之前的值之和除以m的余数,其中i和j的距离为min{|i-j|,n-|i-j|}。给定n,m,d,k和自动机各格子的初始值,你的任务是计算k次操作以后各格子的值。
如下图,n=5,m=3,d=1,一次操作将把图a变成图b。比如,与格子3距离不超过1的格子(即格子2,3,4)在操作之前的值分别是2,2,1,因此从 操作后格子3的值为(2+2+1)mod 3=2。
Input
第一行包含4个整数n,m,d,k。第二行包含n个0..m-1的整数,即各格子的初始值。
Output
输出一行,包含n个0..m-1的整数,即k次操作后各格子的值。
分析:
直接开500x500的矩阵会爆掉,手写一下就会发现其实相伴矩阵每一行都是上一行平移一个单位得到的,所以缩小为一维,直接^k-1次方,然后和原始矩阵乘上就可以了。
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long LL;
const int MAXN=501;
int n,mod,d,k;
struct matrix
{
LL v[MAXN];
matrix()
{
memset(v,0,sizeof(v));
}
friend matrix operator*(matrix a,matrix b)
{
matrix c;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
c.v[i]=(c.v[i]+a.v[j]*b.v[(i-j+n)%n]%mod)%mod;
return c;
}
};
matrix qkpow(matrix a,int b)
{
matrix s=a;
while(b)
{
if(b&1) s=s*a;
a=a*a;
b=b>>1;
}
return s;
}
int main()
{
// freopen("in.txt","r",stdin);
scanf("%d%d%d%d",&n,&mod,&d,&k);
matrix a,f;
for(int i=0;i<n;i++)
scanf("%lld",&f.v[i]);
for(int i=-d;i<=d;i++)
a.v[(i+n)%n]=1;
a=qkpow(a,k-1);
matrix ans;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
ans.v[i]=(ans.v[i]+f.v[j]*a.v[(i-j+n)%n]%mod)%mod;
for(int i=0;i<n;i++)
printf("%lld ",ans.v[i]);
return 0;
}