poj 3150 Cellular Automaton(矩阵连乘)

好久没写博了,最近都没怎么做poj,总是找些三体做做,没局限于哪些知识点,只是想锻炼一下自己独立思考的能力。

好了,废话少说,还是来说说这道题吧。

题意:给出M个数形成环形,一次转化就是将每一个数前后的d个数字的和对m取余,然后作为这个数,问进行k次后的数组是什么。

思路:呃,其实看完这题后,首先想到的事模拟,但是这题的数据量很大,然后就没招了。看了看了discuss了的讨论,看到他们都在讨论矩阵连乘的问题,然后向了半天,貌似有点头绪了,但是不清晰,然后看了解题报告,脑中的想法一下就清晰了,不过当时不知道N*N的矩阵连乘会超时,后来看了别人的优化,才知道自己差的还很多。

说下主要思想吧,对于样例1来说,a[0] = (a[0]*1)+(a[1]*1)+a[2]*0)+(a[3]*0)+(a[4]*1),这样得到一个N*N 的系数矩阵p,即:

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

然后就是用二分求p^k,不过这样做也会超时,所以要找下这个矩阵的规律,做些优化,不过这个规律不是我自己找出来的,就不多说了,可以看一下这个博客,里面讲的很清楚:http://www.cppblog.com/varg-vikernes/archive/2011/02/08/139804.html

代码:

View Code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <math.h>
#define  N 505
using namespace std ;

typedef long long ll ;

ll a[N] , p[N] ;

int n , m , d , k ;

void mult( ll a[] , ll b[] )
{
    ll c[N] ;
    int i , j;
    for ( i = 0 ; i < n ; i++ )
    {
        c[i] = 0;
        for ( j = 0 ; j < n ; j++ )
        {
            c[i] += a[j] * b[i>=j?(i-j) : (n+i-j)] ;
        }
    }
    for ( i = 0 ; i < n ; i++ )
    b[i] = c[i] % m ;
}

int main()
{
    int i ;

    scanf ( "%d%d%d%d" , &n , &m , &d , &k);
    {
        for ( i = 0 ; i < n ; i++ )
        cin>>a[i] ;

        for ( p[0] = i = 1 ; i<= d ; ++i )
        p[i] = p[n-i] = 1 ;

        while ( k )//二分法求p^k
        {
            if( k & 1 )//k为奇数
            mult( p , a );
            mult( p , p );//k为偶数
            k>>=1 ;
        }

        for( i = 0 ; i < n ; i++ )
        {
            if ( i )
            cout<<" "<<( a[i] % m );
            else
            cout<<(a[i] % m ) ;
        }
        cout<<endl;
    }
    return 0 ;
}
原文地址:https://www.cnblogs.com/misty1/p/2686876.html