题目链接:http://acm.hust.edu.cn/vjudge/problem/38668
题意:有一个长度为n的圈,里面有n个数,每一次圈里的每个数会变成与它的距离不超过d的数之和(包括自己),求k次后这个圈里的n个数各是多少。
思路:k很大,只能矩阵快速幂计算,很容易可以构造出来转移矩阵,a1,a2,a3,...an n*n的转移矩阵第i列转移ai去下一个状态,那么aij = 1( abs(i-j)<=d )。
但是存不下500*500的矩阵,而且矩阵的每一列都是相对于前一列整体向下移一位,所以我们只需要保存第一列,就可以记录整个矩阵,矩阵乘法相应的也需要做一下调整。
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <stack>
#include <map>
#include <set>
#include <vector>
#include <sstream>
#include <queue>
#include <utility>
using namespace std;
#define rep(i,j,k) for (int i=j;i<=k;i++)
#define Rrep(i,j,k) for (int i=j;i>=k;i--)
#define Clean(x,y) memset(x,y,sizeof(x))
#define LL long long
#define ULL unsigned long long
#define inf 0x7fffffff
#define mod 100000007
const int maxn = 509;
int n,m,d,k;
struct node
{
LL a[maxn];
void P( int k , int m = 0 )
{
Clean( a , 0 );
a[0] = 1;
rep(j,1,m)
{
a[j] = a[ k - j ] = 1;
}
}
};
node multi( node &x , node &y , int z )
{
node ans;
rep(i,0,z-1)
{
ans.a[i] = 0;
rep(j,0,z-1)
ans.a[i] = ( ans.a[i] + x.a[(j-i+n)%n] * y.a[j] ) % m;
}
return ans;
}
int main()
{
while( scanf("%d%d%d%d",&n,&m,&d,&k) == 4 )
{
LL x[maxn];
rep(i,0,n-1)
{
scanf("%lld",&x[i]);
x[i] %= m;
}
node ans,temp;
ans.P( n );
temp.P( n , d );
while( k )
{
if ( k & 1 ) ans = multi( temp , ans , n );
temp = multi( temp , temp , n );
k >>= 1;
}
rep(i,0,n-1)
{
LL S = 0;
rep(j,0,n-1)
S = ( S + x[j] * ans.a[(j-i+n)%n] ) % m;
printf("%lld%c",S,( i == n-1 )?'\n':' ');
}
}
return 0;
}