好久没写博了,最近都没怎么做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 ; }