题目大意
给定长度为l的只有f,m两种字母 的序列,问不出现fff,fmf的序列个数有多少个
每次的下一个状态都与前一次状态的后两个字母有关
比如我令mm : 0 , mf : 1 , fm : 2 , ff : 3;
那么dp[i][j] 表示长度为i的序列最后由j状态结尾的总个数,当然 j 要大于2
dp[i][0] = dp[i-1][0] + dp[i-1][2]
dp[i][1] = dp[i-1][0]
dp[i][2] = dp[i-1][1] + dp[i-1][3]
dp[i][3] = dp[i-1][1]
根据这个递推关系,我们就能很容易地用动态规划解这道题目,然后就发现超时了 。。。
换个角度把dp值当作矩阵看 (dp[i][0] , dp[i][1] , dp[i][2] , dp[i][3]) = {{1 , 1 , 0 , 0} , {0 , 0 , 1, 1} , {1 , 0 , 0 ,0} , {0 , 0 ,1 , 0}} *(dp[i-1][0] , dp[i-1][1] , dp[i-1][2] , dp[i-1][3])
然后连续乘法上进行优化
while(n){
if(n & 1) ~
~
n>>=1
}
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 using namespace std; 5 int l , M; 6 7 struct Matrix{ 8 int m[4][4]; 9 Matrix operator*(const Matrix &p)const { 10 Matrix tmp; 11 for(int i = 0 ; i < 4 ; i++) 12 for(int j = 0 ; j<4 ; j++){ 13 tmp.m[i][j] = 0; 14 for(int k = 0 ; k<4 ; k++){ 15 tmp.m[i][j] += m[i][k] * p.m[k][j]; 16 tmp.m[i][j] %= M; 17 } 18 } 19 return tmp; 20 } 21 void show(){ 22 for(int i = 0 ; i<4 ; i++){ 23 for(int j = 0 ; j<4 ; j++){ 24 printf("%d " , m[i][j]); 25 } 26 puts(""); 27 } 28 } 29 }; 30 31 Matrix pow(Matrix a , int n) 32 { 33 Matrix tmp; 34 memset(tmp.m , 0 , sizeof(tmp.m)); 35 //建立一个单位矩阵 36 for(int i = 0 ; i<4 ; i++) 37 tmp.m[i][i] = 1; 38 39 while(n){ 40 if(n & 1) tmp = tmp*a; 41 a = a * a; 42 n >>= 1; 43 } 44 return tmp; 45 } 46 47 int main() 48 { 49 while(~scanf("%d%d" , &l , &M)){ 50 if(l == 0) puts("0"); 51 else if(l == 1) printf("%d\n" , 2%M); 52 else{ 53 Matrix a; 54 a.m[0][0] = 1 , a.m[0][1] = 1 , a.m[0][2] = 0 , a.m[0][3] = 0; 55 a.m[1][0] = 0 , a.m[1][1] = 0 , a.m[1][2] = 1 , a.m[1][3] = 1; 56 a.m[2][0] = 1 , a.m[2][1] = 0 , a.m[2][2] = 0 , a.m[2][3] = 0; 57 a.m[3][0] = 0 , a.m[3][1] = 0 , a.m[3][2] = 1 , a.m[3][3] = 0; 58 59 Matrix t = pow(a , l-2); 60 61 int ans = 0; 62 int b[4] = {1 , 1 , 1 , 1}; 63 for(int i = 0 ; i<4 ; i++) 64 for(int j = 0 ; j<4 ; j++){ 65 ans += b[j] * t.m[j][i]; 66 } 67 printf("%d\n" , ans % M); 68 } 69 } 70 return 0; 71 }
时间: 2024-10-13 04:32:33