UVALive 6184_One-Dimensional Cellular Automaton(矩阵快速幂)

题目: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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值