題目鏈接:https://www.nowcoder.com/questionTerminal/79c639e02bc94e6b919e3372c8e1dc5e
小易擁有一個擁有魔力的手環上面有n個數字(構成一個環),當這個魔力手環每次使用魔力的時候就會發生一種奇特的變化:每個數字會變成自己跟后面一個數字的和(最后一個數字的后面一個數字是第一個),一旦某個位置的數字大於等於100就馬上對100取模(比如某個位置變為103,就會自動變為3).現在給出這個魔力手環的構成,請你計算出使用k次魔力之后魔力手環的狀態。
輸入描述:
輸入數據包括兩行: 第一行為兩個整數n(2 ≤ n ≤ 50)和k(1 ≤ k ≤ 2000000000),以空格分隔 第二行為魔力手環初始的n個數,以空格分隔。范圍都在0至99.
輸出描述:
輸出魔力手環使用k次之后的狀態,以空格分隔,行末無空格。
輸入例子:
3 2 1 2 3
輸出例子:
8 9 7
思路:n個數的環進行移動相加,考慮到矩陣行、列變換可以完成這種移動和相加,於是構造出快速冪矩陣,快速冪矩陣M和原矩陣S相乘得到一次移動的結果,那么F(n+1) = M^n*S(這里的冪和乘法是矩陣的運算),很容易得到如下遞推示例:
[[1 1 0] [0 1 1] [1 0 1]]*[[a][b][c]] = [[a+b][b+c][c+a]], 如此,已經確定了快速冪矩陣M,那么就要考慮如何相乘得到最快的冪求解速度。建議參看這篇博客
代碼如下:(算法復雜度為O(n^3*log(k)))
#include<iostream> #include<cstring> using namespace std; int main(){ int n,k; cin >> n >> k; int d[n];//存放結果 for(int i=0;i<n;++i) { cin >> d[i]; } //構造快速冪矩陣 int Mul[n][n]; for(int i=0;i<n-1;++i) { fill(Mul[i],Mul[i]+n,0); Mul[i][i] = 1; Mul[i][i+1] = 1; } fill(Mul[n-1],Mul[n-1]+n,0); Mul[n-1][0] = 1; Mul[n-1][n-1] = 1; //轉化為2進制,進行二分搜索 while(k) { if(k&1) { int temp[n]; fill(temp, temp+n, 0); for(int i=0;i<n;++i) { for(int j=0;j<n;++j) { temp[i] += (Mul[i][j]*d[j]); temp[i] = temp[i]%100; } } memcpy(d, temp, sizeof(d)); } k = k>>1; int temp[n][n]; for(int i=0;i<n;++i) { fill(temp[i],temp[i]+n,0); } for(int i=0;i<n;++i) { for(int j=0;j<n;++j) { for(int k=0;k<n;++k) { temp[i][j]+=Mul[i][k]*Mul[k][j];//二維矩陣相乘 } temp[i][j] %= 100;//快速冪取余中,a^k % c = (a % c)^k % c } } for(int i=0;i<n;++i) { memcpy(Mul[i],temp[i],sizeof(Mul[i])); } } //輸出結果 for(int i=0;i<n-1;++i) { cout << d[i] << ' '; } cout << d[n-1] << endl; return 0; }