POJ 2411:Blocks
POJ 2411:Blocks
题目链接:http://poj.org/problem?id=3734
题目大意:在$1 \times n$网格内填入红色,蓝色,绿色或黄色,问填红色和绿色的网格数均为偶数的情况有多少种。
DP+快速幂
定义状态:
- $a_i$表示前$i$个格子内红色和绿色的网格数为一奇一偶;
- $b_i$表示前$i$个格子内红色和绿色的网格数均为偶数;
- $c_i$表示前$i$个格子内红色和绿色的网格数均为奇数.
当前$i$个格子红色和绿色的网格数为一奇一偶时,考虑下面三种情况:
- 第$i+1$个格子填红色或绿色,使得前$i+1$个格子红色和绿色的网格数均为奇数;
- 第$i+1$个格子填红色或绿色,使得前$i+1$个格子红色和绿色的网格数均为偶数;
- 第$i+1$个格子填蓝色或黄色,使得前$i+1$个格子红色和绿色的网格数为一奇一偶.
从而得到
- $a_i=2a_{i-1}+2b_{i-1}+2c_{i-1}$
- $b_i=a_{i-1}+2b_{i-1}$
- $c_i=a_{i-1}+2c_{i-1}$
构造矩阵即可,复杂度为$O(log_2n)$.
代码如下:
1 #include <cstdio> 2 using namespace std; 3 const int p=10007; 4 int T,n; 5 int mul(int a,int b){return (a*b)%p;} 6 int add(int a,int b){return (a+b)%p;} 7 struct mat{ 8 int mp[3][3]; 9 friend mat operator*(mat a,mat b){ 10 mat c; 11 for(int i=0;i<3;++i){ 12 for(int j=0;j<3;++j){ 13 c.mp[i][j]=0; 14 for(int k=0;k<3;++k) 15 c.mp[i][j]=add(c.mp[i][j],mul(a.mp[i][k],b.mp[k][j])); 16 } 17 } 18 return c; 19 } 20 }E,A; 21 mat pow(mat a,int n){ 22 mat r=E; 23 while(n){ 24 if(n&1)r=r*a; 25 a=a*a; 26 n>>=1; 27 } 28 return r; 29 } 30 void init(){ 31 for(int i=0;i<3;++i) 32 for(int j=0;j<3;++j) 33 E.mp[i][j]=(i==j); 34 A.mp[0][0]=2,A.mp[0][1]=2,A.mp[0][2]=2; 35 A.mp[1][0]=1,A.mp[1][1]=2,A.mp[1][2]=0; 36 A.mp[2][0]=1,A.mp[2][1]=0,A.mp[2][2]=2; 37 } 38 int main(void){ 39 init(); 40 scanf("%d",&T); 41 while(T--){ 42 scanf("%d",&n); 43 mat B=pow(A,n); 44 printf("%d\n",B.mp[1][1]); 45 } 46 }