poj 3150 Cellular Automaton

        题意:一个细胞自动机(名字真高级)包含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;
}


你可能感兴趣的:(矩阵快速幂)