题目链接:http://poj.org/problem?id=3150
题目意思:有n个数围成一个环,现在有一种变换,将所有距离第i(1<=i<=n)个数小于等于d的数加起来,对m取余,现在要求将所有的数都变换k次,得到的n个数的值。
思路:构造一个循环矩阵,以下这个矩阵是以样例1为例的循环矩阵。
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
我们发现n尽然达到了500,复杂度是n^3logk,过不了,我们发现这个矩阵长得很奇葩,每一行都是上一行后移一位得到,所以我们每个矩阵可以n^2算出一行,然后通过平移得到全部的矩阵。从而把n^3的矩阵乘法变成n^2,复杂度是n^2logk,就可以AC了,其他没有什么奇怪的操作。第一道循环矩阵题,算是涨姿势了,循环矩阵乘法模板记住了。
代码:
1 //Author: xiaowuga 2 #include<iostream> 3 #include<cstring> 4 #define maxx INT_MAX 5 #define minn INT_MIN 6 #define inf 0x3f3f3f3f 7 #define N 505 8 using namespace std; 9 long long MOD; 10 typedef long long ll; 11 long long n,size;//第n项,矩阵大小 12 long long f[550]; 13 struct Matrix{ 14 long long mat[N][N]; 15 void clear(){ 16 memset(mat,0,sizeof(mat)); 17 } 18 Matrix operator * (const Matrix & m) const{ 19 Matrix tmp; 20 tmp.clear(); 21 for(int i=0;i<size;i++){ 22 for(int j=0;j<size;j++){ 23 tmp.mat[0][i]+=mat[0][j]*m.mat[j][i]%MOD; 24 } 25 tmp.mat[0][i]%=MOD; 26 } 27 for(int i=1;i<size;i++){ 28 for(int j=0;j<size;j++) 29 tmp.mat[i][j]=tmp.mat[i-1][(j+size-1)%size]; 30 } 31 return tmp; 32 } 33 }M,ANS; 34 Matrix ans; 35 void POW(Matrix m,ll k){ 36 memset(ans.mat,0,sizeof(ans.mat)); 37 for(int i=0;i<size;i++) ans.mat[i][i]=1; 38 while(k){ 39 if(k&1) ans=ans*m; 40 k/=2; 41 m=m*m; 42 } 43 long long sum; 44 for(int i=0;i<size;i++){ 45 sum=0; 46 for(int j=0;j<size;j++){ 47 sum+=ans.mat[i][j]*f[j]%MOD; 48 } 49 if(i==0) cout<<sum%MOD; 50 else cout<<" "<<sum%MOD; 51 } 52 cout<<endl; 53 } 54 int main() { 55 ios::sync_with_stdio(false);cin.tie(0); 56 long long d; 57 while(cin>>size>>MOD>>d>>n){ 58 M.clear(); 59 for(int i=0;i<size;i++) cin>>f[i]; 60 for(int i=0;i<size;i++){ 61 M.mat[i][i]=1; 62 for(int j=1;j<=d;j++){ 63 M.mat[i][(i+j+size)%size]=1; 64 M.mat[i][(i-j+size)%size]=1; 65 } 66 } 67 for(int i=0;i<size;i++){ 68 for(int j=0;j<size;j++) cout<<M.mat[i][j]<<' '; 69 cout<<endl; 70 } 71 POW(M,n); 72 } 73 return 0; 74 }