题目链接:POJ3150
【题意】给出一个有N(<=500)个数字的数列,求置换,每次置换为当前元素加上左右两边的d(<n/2)个数的和;
【分析】对于N=5,d=1,很容易推出一个N*N的矩阵:
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
如果直接用这个矩阵做快速幂的话,时间复杂度是log(k)*N^3的,肯定超时了;
那么必须要优化,观察上面矩阵发现,这个矩阵的下一行是上一行右移一个单位;这样的话就可以在矩阵乘法的时候只保存第一行,假如用a[],保存第一行,则它做乘法的时候只需要a[i]*a[(i-1)%n]即可,因为上下左右都是循环的;
这样复杂度就降低到了log(k)*N^2,不会超时了;据说还有log(k)*N*log(N)的算法,表示想不到,以后再说;
【AC CODE】2282ms
#include <cstdio>
#include <cstring>
#include <cctype>
#include <cmath>
#include <map>
//#include <unordered_map>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
typedef long long LL;
#define rep(i,a,n) for(int i = a; i < n; i++)
#define repe(i,a,n) for(int i = a; i <= n; i++)
#define per(i,n,a) for(int i = n; i >= a; i--)
#define clc(a,b) memset(a,b,sizeof(a))
const int INF = 0x3f3f3f3f, MAXN = 500+10;
int n, MOD,a[MAXN],buf[MAXN];
void mul(int *ans, int *a, int *b)
{
clc(buf,0);
rep(i,0,n)
{
rep(s,0,n)
{
int j = (i-s+n)%n;
buf[i] += (LL)a[j]*(LL)b[s]%MOD;
}
buf[i] %= MOD;
}
memcpy(ans,buf,sizeof(int)*n);
}
void pow_mod(int *ans, int *x, int c)
{
int tmp[MAXN];
clc(tmp,0);
tmp[0] = 1;
while(c)
{
if(c&1) mul(tmp,tmp,x);
mul(x,x,x);
c >>= 1;
}
memcpy(ans,tmp,sizeof(int)*n);
}
int main()
{
#ifdef SHY
freopen("e:\\1.txt", "r", stdin);
#endif
int d,k,x[MAXN];
while(~scanf("%d %d %d %d%*c", &n, &MOD, &d, &k))
{
clc(x,0);
rep(i,0,n) scanf("%d%*c", &a[i]);
repe(i,0,d) x[i] = 1;
rep(i,n-d,n) x[i] = 1;
pow_mod(x,x,k);
mul(x,x,a);
printf("%d", x[0]);
rep(i,1,n) printf(" %d", x[i]);
putchar('\n');
}
return 0;
}