题目:http://poj.org/problem?id=3734
刚开始看到题目N<10^9吓蒙了都,看了discuss才明白,还是对DP理解的太浅呢。。。
分析:我们更多的关心红色和绿色的情况,限制条件是两者个数都是偶数,但子问题却可能出现两者一奇一偶、一偶一奇、两奇的情况,假设已经给n个块染色,且出现这4种情况的场面分别有{a, b, c, d}个,给第n+1个块染色之后可能构成的场面和数量分别为{m, n, p, q},我们来看两者是不是有一定的关系:
1、如果给第n+1个块染红色,则两偶 -> 一奇一偶;一奇一偶 -> 两偶;一偶一奇 -> 两奇;两奇 -> 一偶一奇。
2、如果给第n+1个块染绿色,则两偶 -> 一偶一奇;一奇一偶 -> 两奇;一偶一奇 -> 两偶;两奇 -> 一奇一偶。
3、如果给第n+1个块染黄色,则两偶 -> 两偶;一奇一偶 -> 一奇一偶;一偶一奇 -> 一偶一奇;两奇 -> 两奇。
4、如果给第n+1个块染蓝色,则两偶 -> 两偶;一奇一偶 -> 一奇一偶;一偶一奇 -> 一偶一奇;两奇 -> 两奇。
所以状态n+1的数量可以从状态n的数量中推出来,具体而言,{m, n, p, q}和{a, b, c, d}之间的关系就是:
m = b + c + a + a = 2a + b + c
n = a + d + b + b = a + 2b + d
p = d + a + c + c = a + 2c + d
q = c + b + d + d = b + c + 2d
有点fibonacci的感觉我们有,f[n+1] = f[n] * m,其中m是递推矩阵, m = [[2, 1, 1, 0], [1, 2, 0, 1], [1, 0, 2, 1], [0, 1, 1, 2]],初始条件f[1] = [2, 1, 1, 0],即
1、第一格染黄色或蓝色 => 两偶(都是0),有2种情况
2、第一格染红色 => 一奇一偶(红1绿0),有1种情况
3、第一格染绿色 => 一偶一奇(红0绿1),有1种情况
4、第一格不可能让红和绿都是奇数
从而f[n] = f[1] * m ^ (n-1),用矩阵快速幂可以达到O(logn)复杂度。
#include <cstdio>
#include <cstring>
#define MOD 10007
const int transfer[][4] = {{2, 1, 1, 0}, {1, 2, 0, 1}, {1, 0, 2, 1}, {0, 1, 1, 2}};
struct Matrix{
int a[4][4];
Matrix operator * (const Matrix& m)const{
Matrix t;
int i, j, k, s;
for(i = 0; i < 4; ++i){
for(j = 0; j < 4; ++j){
for(s = k = 0; k < 4; ++k) s = (s + a[i][k] * m.a[k][j]) % MOD;
t.a[i][j] = s;
}
}
return t;
}
};
Matrix power(const Matrix& matrix, int n)
{
if(n == 1) return matrix;
Matrix tmp = power(matrix, n >> 1);
if(n & 1) return tmp * tmp * matrix;
return tmp * tmp;
}
int main()
{
int t, n;
Matrix matrix, result;
memcpy(matrix.a, transfer, sizeof(matrix.a));
for(scanf("%d", &t); t--; ){
scanf("%d", &n);
if(n == 1){
puts("2");
continue;
}
result = power(matrix, n-1);
printf("%d\n", (result.a[0][0]*2 + result.a[1][0] + result.a[2][0]) % MOD);
}
return 0;
}