poj3150 Cellular Automaton(矩阵快速幂)

对于样例:

5311

12212

使得

a = {1 2 2 1 2};
b = {
1 1 0 0 1
1 1 1 0 0
0 1 1 1 0
0 0 1 1 1
1 0 0 1 1

}

变换一次,矩阵相乘 a * b = 5 5 5 5 4;各项对取余就是结果;

如果变换k次,结果就是a×(b^k);所以用矩阵快速幂计算b^n;

但是n×n的矩阵会超时啊,而且要long long[500+][500+],数组太大,连本地都开不开。

但是矩阵b比较对称,可以发现

b^1 =
[1, 1, 0, 0, 1]
[1, 1, 1, 0, 0]
[0, 1, 1, 1, 0]
[0, 0, 1, 1, 1]
[1, 0, 0, 1, 1]
b^2 =
[3, 2, 1, 1, 2]
[2, 3, 2, 1, 1]
[1, 2, 3, 2, 1]
[1, 1, 2, 3, 2]
[2, 1, 1, 2, 3]
b^3 =
[7, 6, 4, 4, 6]
[6, 7, 6, 4, 4]
[4, 6, 7, 6, 4]
[4, 4, 6, 7, 6]
[6, 4, 4, 6, 7]
b^4 =
[19, 17, 14, 14, 17]
[17, 19, 17, 14, 14]
[14, 17, 19, 17, 14]
[14, 14, 17, 19, 17]
[17, 14, 14, 17, 19]

第i行是第i-1行向右平移一位。所以计算和存储都只关于第一行就行了。

数据较大,取余太多,用时就比较多,尽量少取余。


#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
#define LL long long
#define maxn 550

using namespace std;
int MOD,N;
struct Mat{
	LL at[maxn];
	int n;
	Mat(){n=N;}
};
Mat mul(const Mat &a,const Mat &b)
{
	Mat t;
	memset(t.at,0,sizeof(t.at));
	int p=0;
	for(int i=0;i<a.n;++i)
	{
		for(int j=0;j<a.n;++j)
		{
            if(p==a.n)p=0;
			t.at[i]+=(a.at[j]*b.at[p++]);
		}
		p--;
		t.at[i]%=MOD;
	}
	return t;
}
Mat _pow (Mat p,int k)
{
	if(k==1)return p;
	Mat e;
	memset(e.at,0,sizeof(e.at));
	e.at[0]=1;
	if(k==0)return e;
	while(k)
	{
		if(k&1)e=mul(e,p);
		p=mul(p,p);
		k>>=1;
	}
	return e;
}
int main()
{
	LL arr[maxn];
	int n,d,k;
	while(~scanf("%d%d%d%d",&N,&MOD,&d,&k))
	{
		n=N;
		Mat tmp;
		memset(tmp.at,0,sizeof(tmp.at));
		tmp.at[0]=1;
		for(int i=1;i<=d;++i)
		{
				tmp.at[i]=1;
				int pos=-i;
				if(pos<0)pos+=n;
				tmp.at[pos]=1;
		}
		tmp=_pow(tmp,k);
		for(int i=0;i<n;++i)
		{
			scanf("%lld",&arr[i]);
		}
		int p=0;
		for(int i=0;i<n;++i)
		{
			LL ans=0;
			for(int j=0;j<n;++j)
			{
				if(p==n)p=0;
				ans+=arr[j]*tmp.at[p++];
			}
			p--;
			ans%=MOD;
			if(i!=0)
				printf(" ");
			printf("%lld",ans);
		}
		printf("\n");
	}
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值