题意 : 给出n个数形成环形,一次转化就是将每一个数前后的d个数字的和对m取余,然后作为这个数,问进行k次转化后,数组变成什么。
思路 :
首先来看一下Sample里的第一组数据。
1 2 2 1 2经过一次变换之后就成了5 5 5 5 4它的原理就是a0 a1 a2 a3 a4->(a4+a0+a1) (a0+a1+a2) (a1+a2+a3) (a2+a3+a4) (a3+a4+a0)如果用矩阵相乘来描述,那就可以表述为1xN和NxN的矩阵相乘,结果仍为1xN矩阵a = 1 2 2 1 2 b = 1 1 0 0 11 1 1 0 00 1 1 1 00 0 1 1 11 0 0 1 1a * b = 5 5 5 5 4所以最终结果就是:a * (b^k)线性代数不合格的同鞋表示压力很大。。对一个NxN矩阵求k次方,而且这个k很大,N也不小,怎么办?所以有高手观察到了,这个矩阵长得有点特殊,可以找到一些规律:b^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]b^2 =[3, 2, 1, 1, 2][2, 3, 2, 1, 1][1, 2, 3, 2, 1][1, 1, 2, 3, 2][2, 1, 1, 2, 3]b^3 =[7, 6, 4, 4, 6][6, 7, 6, 4, 4][4, 6, 7, 6, 4][4, 4, 6, 7, 6][6, 4, 4, 6, 7]b^4 =[19, 17, 14, 14, 17][17, 19, 17, 14, 14][14, 17, 19, 17, 14][14, 14, 17, 19, 17][17, 14, 14, 17, 19]发现神马没有。就是无论是b的几次幂,都符合A[i][j] = A[i-1][j-1]高手说是这样推倒出来地:““”利用矩阵A,B具有a[i][j]=A[i-1][j-1],B[i][j]=B[i-1][j-1](i-1<0则表示i-1+n,j-1<0则表示j-1+n)我们可以得出矩阵C=a*b也具有这个性质C[i][j]=sum(A[i][t]*B[t][j])=sum(A[i-1][t-1],B[t-1][j-1])=sum(A[i-1][t],B[t][j-1])=C[i-1][j-1] “”“这样就可以开一个N大小的数组来存放每次计算的结果了。而没必要用NxN。N的问题解决了,但是k还是很大,怎么办?这时候可以用二分法来求b^kb^k = b^1 * b^4 * b^16 。。。1 //3150 2 #include3 #include 4 #include 5 #define LL long long 6 7 using namespace std ; 8 9 int n,m , d , k ;10 LL a[1010] ,b[1010] ;11 void multi(LL *c,LL *d)12 {13 LL x[1010] ;14 for(int i = 0 ; i < n ; i++)15 {16 x[i] = 0 ;17 for(int j = 0 ; j < n ; j++)18 x[i] += c[j] * d[i >= j ? (i - j) : (n + i - j)] ;//防止是负数,形成环19 }20 for(int i = 0 ; i < n ; i++)21 d[i] = x[i] % m ;22 }23 int main()24 {25 while(cin >> n >> m >> d >> k ){26 for(int i = 0 ; i < n ; i++)27 cin >> a[i] ;28 b[0] = 1 ;29 for(int i = 1 ; i <= d ; i++)30 b[i] = b[n - i] = 1 ;31 while(k)32 {33 if(k & 1)//奇数34 multi(b,a) ;35 multi(b,b) ;36 k >>= 1 ;37 }38 for(int i = 0 ; i < n ; i++)39 if(i == n-1) printf("%I64d\n",a[i]) ;40 else printf("%I64d ",a[i]) ;41 }42 return 0 ;43 }
计算过程中,必定会出现数字大于M的情况。切记 x*y = (x%M)*(y%M)