题目:UVALive 6184
题型:数论
题意:
有n个数,初始状态为S1~Sn,随着时间t变化,规则为:
S(i,t+1) = (A*S(i-1,t) + B*S(i,t) + C*S(i+1,t)) % m (对于S1和Sn的计算,S0,Sn+1=0)
现在问T时间后这n个数的值。
分析:
由于T范围是10^9,所以看完这道题就觉得应该通过循环结找规律,可是辛辛苦苦写了一个代码发现一直TLE,就感觉可能对于某些样例,循环结太大,无法暴力找出来。后来想到关于递推,可以采用矩阵快速幂来做,唉,总是不碰这些东西,用的时候就几乎想不起来了-_-|||
根据递推式,可以建立如下矩阵:
/ \ ^ T / \
| 0 0 0 0 ...... 0 | | 0 |
| a b c 0 ...... 0 | | S1 |
| 0 a b c ...... 0 | | S2 |
| 0 0 a b ...... 0 | | S3 |
| 0 0 0 a ...... 0 | | S4 |
| . . . . . . | * | . | = ?
| . . . . . . | | . |
| . . . . . . | | . |
| 0 0 0 0 ...... c | | Sn |
| 0 0 0 0 ...... 0 | | 0 |
\ / \ /
时间复杂度为O((n+1)^2*log(T))
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int M=105;
int n,m,a,b,c,t;
struct Matrix{
int val[M][M];
void zero(){
memset(val,0,sizeof(val));
}
void unit(){
zero();
for(int i=0;i<M;i++) val[i][i]=1;
}
}T;
Matrix operator * (const Matrix &a,const Matrix &b){
Matrix tmp;
tmp.zero();
for(int k=0;k<=n;k++)
for(int i=0;i<=n;i++){
if(a.val[i][k])
for(int j=0;j<=n;j++){
tmp.val[i][j]+=(a.val[i][k]%m)*(b.val[k][j]%m);
tmp.val[i][j]%=m;
}
}
return tmp;
}
Matrix operator ^ (Matrix &a,int p){
Matrix tmp;
tmp.unit();
while(p){
if(p&1) tmp=tmp*a;
a=a*a;
p>>=1;
}
return tmp;
}
int A[M];
int sum[M];
int main(){
while(~scanf("%d%d%d%d%d%d",&n,&m,&a,&b,&c,&t),n+m+a+b+c+t){
T.zero();
for(int i=1;i<n+1;i++){
T.val[i][i-1]=a;
T.val[i][i]=b;
T.val[i][i+1]=c;
}
for(int i=1;i<=n;i++){
scanf("%d",&A[i]);
}
if(t==0){
for(int i=1;i<=n;i++){
if(i>1) printf(" ");
printf("%d",A[i]);
}
puts("");
continue;
}
T=T^t;
memset(sum,0,sizeof(sum));
A[0]=A[n+1]=0;
for(int i=0;i<=n+1;i++){
for(int j=0;j<=n+1;j++){
sum[i]+=T.val[i][j]*A[j];
sum[i]%=m;
}
}
for(int i=1;i<=n;i++){
if(i>1) printf(" ");
printf("%d",sum[i]);
}
puts("");
}
return 0;
}