POJ 3734 Blocks (动态规划) (矩阵幂)

来源:互联网 发布:2017php应聘要求 编辑:程序博客网 时间:2024/05/29 17:35
Blocks
Time Limit: 1000MS Memory Limit: 65536KTotal Submissions: 6976 Accepted: 3370

Description

Panda has received an assignment of painting a line of blocks. Since Panda is such an intelligent boy, he starts to think of a math problem of painting. Suppose there are N blocks in a line and each block can be paint red, blue, green or yellow. For some myterious reasons, Panda want both the number of red blocks and green blocks to be even numbers. Under such conditions, Panda wants to know the number of different ways to paint these blocks.

Input

The first line of the input contains an integer T(1≤T≤100), the number of test cases. Each of the next T lines contains an integer N(1≤N≤10^9) indicating the number of blocks.

Output

For each test cases, output the number of ways to paint the blocks in a single line. Since the answer may be quite large, you have to module it by 10007.

Sample Input

212

Sample Output

26

Source

PKU Campus 2009 (POJ Monthly Contest – 2009.05.17), Simon



问题描述:

第一行给出测试个数

接下来每行给出一个数字n

给定n个方块排成一列,现在要用红 蓝 绿 黄四种颜色的油漆给这些方块涂色,求染成红色的方块和染成绿色的方块个数同时为偶数的染色方案的个数 输出对10007取余后的方案


分析:

到第i+1个方块为止

前i个方块此时的情况只有三种:

①红色 绿色方块都为偶数个 记为ai

②红色 绿色方块恰有一个为偶数个,另一个为奇数个 记为bi

③红色 绿色方块均为奇数个 记为ci


那么对于第i+1种方块

要使

①红色绿色均为偶数个也就是ai+1

有这样几种情况:

(1)前i个方块红色绿色均为偶数个 那么第i+1个染色到蓝色或黄色 共 2*ai种情况

(2)前i个方块红色 绿色方块恰有一个为偶数个,另一个为奇数个 那么第i+1个只能染色到这个奇数颜色方块上 只有bi种情况

综上

ai+1 = 2 * ai + 1 * bi + 0 * ci

②红色 绿色方块恰有一个为偶数个,另一个为奇数个 也就是bi+1

有这样几种情况:

(1)前i个方块红色绿色均为偶数个 那么第i+1个染色到红色或绿色上 使这个颜色变为奇数个 共2*ai种情况

(2)前i个方块红色 绿色方块恰有一个为偶数个,另一个为奇数个   那么第i+1个染色到蓝色或黄色上 不会影响红和绿色 共2*bi种情况

(3)前i个方块红色 绿色均为奇数个那么第i+1个染色到红色或绿色上 使这个颜色变为偶数个 共2*ci种情况

综上

bi+1 = 2 * ai + 2 * bi + 2* ci

③红色绿色方块均为奇数个 也就是ci+1

有这样几种情况:
(1)前i个方块红色 绿色方块恰有一个为偶数个,另一个为奇数个 第i+1个染色到偶数个的那个颜色 把它变为奇数个 共bi种情况

(3)前i个方块红色 绿色均为奇数个 那么第i+1个染色到蓝色或黄色上 不会影响红和绿色 共 2*ci种情况

综上

ci+1 = bi + 2*ci


那么用矩阵表示出来就是:


{ai+1}       {2  1   0}   {ai}

{bi+1}   =  {2  2  2} * {bi}

{ci+1}       {0  1   2}    {ci}


{ai}       { 2  1  0 }i    {a0} 

{bi}  =   { 2  2  2 }  * {b0}

{ci}       {  0  1  2 }     {c0}


{ai}       { 2  1  0 }i    {1} 

{bi}  =   { 2  2  2 }  * {0}

{ci}       {  0  1  2 }     {0}


那么我们要求ai 只要求这个结果矩阵的第一行第一个元素即可


核心就是如何计算矩阵的i次方


代码:


#include <cstdio>#include <cstring>#include <algorithm>#include <vector>using namespace std;typedef vector<int> vec;typedef vector<vec> mat;int n;mat mul(mat& A ,mat& B){// mat C // C是这样的结构 vector<vector<int> > // 这样 每行,每列均为一个变长的数组 模拟成一个矩阵// A * B 那么 结果C的行数与A相同 A.size()就是A的行数// 列数与B相同 那么C的列数就是B的列数 B[0].size()// 循环进行的是矩阵乘法 mat C(A.size(),vec(B[0].size()));for(int i = 0;i<A.size();i++){for(int k = 0;k<B.size();k++){for(int j = 0;j<B[0].size();j++){C[i][j] = (C[i][j] + A[i][k] * B[k][j]) % 10007;}}}return C;}mat pow(mat A,long long n){mat B(A.size(),vec(A.size()));for(int i = 0 ;i<A.size();i++){B[i][i] = 1;   //矩阵E 相当于实数运算的 "1" }while(n>0){if(n & 1)      //使用快速幂的思想来计算出矩阵A的n次方 {B = mul(B,A);}A =mul(A,A);n >>= 1;}return B;}void solve(){mat A(3,vec(3));A[0][0] = 2; A[0][1] = 1; A[0][2] = 0; A[1][0] = 2; A[1][1] = 2; A[1][2] = 2; A[2][0] = 0; A[2][1] = 1; A[2][2] = 2;A = pow(A,n);printf("%d\n",A[0][0]);}int main(){int Case;scanf("%d",&Case);while(Case--){scanf("%d",&n);solve();}}