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 }

 

转载于:https://www.cnblogs.com/barrier/p/6770116.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值