【题解】UVA1386 Cellular Automaton(循环矩阵)

前往:我自己搭建的博客

题目

洛谷UVA1386 Cellular Automaton

题解

由于操作数(k)较大,不可能一轮轮统计,所以考虑矩阵快速幂,但矩阵乘法的时间复杂度是,还是会超时。

观察转移矩阵的性质,会发现它很有规律,是循环矩阵。循环矩阵定义及性质:1.行向量的每个元素都是前一个行向量各元素依次右移一个位置得到的结果。 2.若a,b是循环矩阵,则a+b,a*b都是循环矩阵。 结论:循环矩阵可以压缩为一个列向量。

在矩阵乘法的运算中,若两个矩阵都为压缩矩阵,则无法直接进行运算,需要将左乘矩阵边解压边运算。 如何解压?找规律。(找规律往往比逻辑推理更简便)假设a矩阵压缩后为b向量。

a[1][1]=a[2][2]=a[3][3]=……=a[n][n]=b[1]

a[1][n]=a[2][1]=a[3][2]=……=a[n][n-1]=b[2]

a[1][n-1]=a[2][n]=a[3][1]=……=a[n][n-2]=b[3]

……

a[1][2]=a[2][3]=a[3][4]=……=a[n][1]=b[n]

结论:a[i][j]=b[k] , k=(n+1+i-j) mod n 注意加n是为了确保k为正数。注意环形数据中的距离计算技巧。注意:若n+1+i-j=n,取模后k=0,一般数据在环上的问题,下标设为0~n-1比较方便,此处需要特判。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=500+3;
ll n,MOD,d,k;
struct matrix{ll s[maxn];}nxt,st;	//st是初始矩阵,nxt是压缩成列向量的转移矩阵 
inline matrix mul(matrix a,matrix b) //a,b是两个列向量,将a边解压边与b运算 
{
	matrix c; memset(c.s,0,sizeof(c.s));
	for(int i=1;i<=n;i++)
		for(int k=1;k<=n;k++)
		{
			int t=(n+1+i-k)%n; if(!t) t=n;
			c.s[i]=(c.s[i]+a.s[t]*b.s[k]%MOD)%MOD;
		}
	return c;
}
inline matrix quick_pow(matrix a,ll b)
{
	matrix c; //单位矩阵也是循环矩阵
	memset(c.s,0,sizeof(c.s)); c.s[1]=1;
	for( ;b;b>>=1)
	{
		if(b&1) c=mul(a,c);
		a=mul(a,a);
	}
	return c;
}
inline void solve()
{
	for(int i=1;i<=n;i++) nxt.s[i]=abs(i-1)<=d||n-abs(i-1)<=d;
	for(int i=1;i<=n;i++) scanf("%lld",&st.s[i]);
	nxt=quick_pow(nxt,k);
	st=mul(nxt,st);
	for(int i=1;i<n;i++) printf("%lld ",st.s[i]);
	printf("%lld\n",st.s[n]);
}

int main()
{
	while(scanf("%lld%lld%lld%lld",&n,&MOD,&d,&k)!=EOF) solve();
	//不确定数据组数时的写法 
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/zjgmartin/article/details/108415652