poj3150--Cellular Automaton(矩阵优化)

题目链接:点击打开链接

题目大意:有n个数围成一个环,现在有一种变换,将所有距离第i(1<=i<=n)个数小于等于d的数加起来,对m取余,现在要求将所有的数都变换k次,得到的n个数的值。

一看到那么多数字同时变换,还要变换那么多次,首先想到了用矩阵相乘表示变换后的结果,然后用矩阵快速幂来快速计算,事实告诉我想简单了,,,(PS:一直到敲完才发现,数据太大,根本无法编译)。

可以用一组样例解释怎么做:

样例1
5 3 1 1
1 2 2 1 2
给出了5个数。a[1 2 2 1 2],那么求一次变换后的结果就是a1+a2+a5,a1+a2+a3,a2+a3+a4,a3+a4+a5,a4+a5+a1,然后对5个数取余,这样就可以看出相乘的矩阵

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

最终矩阵是这样的,经过k次变换,只要让原始数据乘以k次这个矩阵就可以,但是n的大小是500,复杂度是n*n*nlog(m),一定会超时,而且内存放不下。

最后发现,对于每一行来说,值是在不断的移动,每次都移动1个格,所以我们可以可以可以只计算一行或者一列,在用到其它行(列)的时候,有计算的这个行(列)进行推导,就可以计算出结果。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std ;
#define LL __int64
#pragma comment(linker, "/STACK:102400000,102400000")
struct node{
    LL a[510] ;
}p , q ;
LL a[510] , n , m , d , mod , ans[510] ;
node mul(node p,node q) {
    node s ;
    int i , j , k , l ;
    for(i = 1 ; i <= n ; i++) {
        s.a[i] = 0 ;
        j = i ;
        for(k = 0 ; k < n ; k++) {
            l = j+k > n ? j+k-n : j+k ;
            s.a[i] = (s.a[i] + p.a[k+1]*q.a[ l ]) % mod ;
        }
    }
    return s ;
}
void pow() {
    memset(q.a,0,sizeof(q.a)) ;
    q.a[1] = 1 ;
    while( m ) {
        if( m%2 )
            q = mul(q,p) ;
        m /= 2 ;
        p = mul(p,p) ;
    }
}
int main() {
    int i , j , k , l ;
    while( scanf("%I64d %I64d %I64d %I64d", &n, &mod, &d, &m) != EOF ) {
        for(i = 1 ; i <= n ; i++)
            scanf("%I64d", &a[i]) ;
        memset(p.a,0,sizeof(p.a)) ;
        for(j = 1 ; j <= n ; j++) {
            if( abs(j-1) <= d || n-abs(j-1) <= d)
                p.a[j] = 1 ;
        }
        pow() ;
        for(i = 1 ; i <= n ; i++) {
            ans[i] = 0 ;
            j = i ;
            for(k = 0 ; k < n ; k++) {
                l = j+k > n ? j+k-n : j+k ;
                ans[i] = (ans[i] + q.a[ l ]*a[ k+1 ])%mod ;
            }
        }
        for(i = 2 ; i <= n+2-i ; i++)
            swap(ans[i],ans[n+2-i]) ;
        for(i = 1 ; i <= n ; i++) {
            if( i < n )
                printf("%I64d ", ans[i]) ;
            else
                printf("%I64d\n", ans[i]) ;
        }
    }
    return 0 ;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值