矩陣快速冪的練習題,需要將題目轉化一下才能用矩陣快速冪。
題目大意:有n個燈,分別編號 1 到 n,它們構成一個環,1 的左邊是 n,k 的左邊是 k-1。時間每過 1 s,如果燈 i 左邊的燈是亮的,則燈 i 切換狀態(1 表示亮,0 表示暗)。附鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=2276。
大體思路:這裏的難點在於如何想到用快速冪,因爲題目所給的秒數很大,單純模擬的話肯定超時,因此可以用矩陣快速冪來解。不過,構造矩陣也需要一點時間,這也可以算作難點,然後是一個小技巧,就是矩陣相乘後每一位與 2 相餘,這一點想到了的話用矩陣快速冪的思路也就清晰了。構造矩陣需要想到燈 i 的狀態和燈 i 左邊的狀態有聯繫,接着構造一個n×n 方,對角線爲 1,代表燈 i 自身,對角線的上方就是燈 i 左邊的燈,也置爲1,由此,矩陣構造完畢。係數矩陣C如下圖所示
1 | 1 | |||||
1 | 1 | |||||
1 | 1 | |||||
1 | 1 | |||||
1 | 1 | |||||
1 | 1 | |||||
1 | 1 |
而開始的矩陣爲題目給的燈初始狀態T。則該題目就轉爲求 T*C^M。
以下爲ac代碼:
#include<iostream>
#include<cstring>
using namespace std;
const int maxn=105;
const int mod=2; //這一點很巧妙,沒有餘數爲2,矩陣快速冪也就沒法應用在該題上
int n;
struct Matrix{
long long mat[maxn][maxn];
};
Matrix unit; //單位矩陣
Matrix mul(Matrix a,Matrix b){
Matrix c;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++){
c.mat[i][j]=0;
for(int k=0;k<n;k++){
c.mat[i][j]+=a.mat[i][k]*b.mat[k][j];
c.mat[i][j]%=mod;
}
}
return c;
}
Matrix mat_pow(Matrix a,long long p){
Matrix temp=unit;
while(p){
if(p%2)
temp=mul(temp,a);
a=mul(a,a);
p/=2;
}
return temp;
}
int main(){
long long M;
char s[maxn];
memset(unit.mat,0,sizeof(unit.mat));
for(int i=0;i<maxn;i++) //構造單位矩陣
unit.mat[i][i]=1;
while(cin>>M){
cin>>s;
n=strlen(s);
Matrix T,C;
for(int i=0;i<n;i++)
T.mat[0][i]=s[i]-'0'; //初始矩陣T
memset(C.mat,0,sizeof(C.mat));
for(int i=1;i<n;i++) //構造係數矩陣
C.mat[i][i]=C.mat[i-1][i]=1;
C.mat[0][0]=C.mat[n-1][0]=1;
Matrix D=mat_pow(C,M); //係數矩陣快速冪
D=mul(T,D); //求最後結果
for(int i=0;i<n;i++)
cout<<D.mat[0][i];
cout<<endl;
}
return 0;
}